自動解析 github倉庫的目錄列表

我在 github上有個維護時間比較長的 repository,開始時只有幾個文件,後來文件數目逐漸增多,期間整理了好幾回,如今已經整理成了好幾個文件夾了,有時候想找某個文件的時候,可是不肯定到底在哪一個文件夾裏面,因而就憑感受一個一個文件夾試過去,層級少點還好,可是層級一多,就算是明確知道在哪一個文件夾裏,一層層點進去也要點好幾回html

因而心中一動,就想着把當前倉庫的目錄結構列出來,直接寫在 README.md文件上,想看哪一個文件直接點,一次點擊便可,手寫目錄確定是不太友好的,由於我可能頻繁增刪文件,甚至是再次整理文件結構,並且也不具有通用性,萬一哪天又想把另一個倉庫也列出目錄結構,那麼又要手寫一遍,因此最好寫個代碼程序來幫我完成這種工做node

先看效果圖:git

遞歸獲取全部文件路徑

目標是輸出目錄的層級結構,那麼首先要把當前倉庫根目錄下全部文件的路徑獲取到,思路很清晰的,先用 fs.readdirSync讀取目錄,而且遞歸循環子目錄,直到最後一層es6

function getDirStruct(basePath = __dirname) {
  const files = fs.readdirSync(basePath)
  files.forEach(file => {
    // 處理先不要顯示的文件
    if (excludeFile.indexOf(file) !== -1 || excludePrefix.some(pre => file.indexOf(pre) === 0)) return
    const fullPath = path.resolve(basePath, file)
    const fileStats = fs.statSync(fullPath)
    // 若是是文件夾,則繼續遍歷其子文件
    return fileStats.isDirectory(file) ? getDirStruct(fullPath) : absolutePath.push(fullPath)
  })
}
複製代碼

這裏獲取到的是全部文件在本地目錄的絕對全路徑,可是後面是須要把這個東西上傳到 github的,因此須要把這個絕對路徑改成相對路徑,用於拼接文件的 url地址github

// 絕對路徑轉相對路徑
const rPath = path.relative(__dirname, apath)
複製代碼

這裏有幾個小點須要注意下編程

  • 排除無用的干擾文件

程序不可能直接運行在 github頁面上的,因此你須要把倉庫下載下來,再本地目錄中運行程序,那麼由於使用了 git的緣故,因此根目錄中確定存在一個 .git文件夾,這個文件夾裏的東西不少,並且你也不太可能但願展現這個東西,因此最好排除掉小程序

相似的還有一些 img文件夾,裏面存了不少圖片,你可能也不想展現出來,由於太佔篇幅了並且也沒什麼用,因此也要排除掉windows

  • 不一樣平臺的路徑分隔符

不一樣平臺上的文件路徑分隔符是不同的,在 windows上 路徑分隔符是 \,而在 POSIX(即類 UNIX系統,包括 MacLinux)上則是 /,因此須要區別處理數組

nodejs中可經過 path.sep來獲取當前操做系統的文件路徑分隔符性能優化

  • 遞歸

文件的逐級讀取涉及到遞歸操做,若是目錄層級不是深到使人智熄的地步,那麼除了程序運行時間比較長之外,不會有什麼問題,可是若是是讀取相似於 node_modules這種文件夾,並且嵌套很深,那麼就可能致使 棧溢出,程序直接 boom

那麼這裏就不得不提到 尾遞歸了,尾遞歸就能很好地避免 棧溢出問題,關於 尾遞歸,參見 知乎:什麼是尾遞歸?

目錄樹(層級結構)

獲取到了全部文件路徑以後,須要對這些文件路徑按照進行整理,獲得一棵 Dir Tree,就是一個用於描述這些文件路徑的層級結構的數據

這裏構建的 Dir Tree相似下述結構:

{
  _children: ['README.md', 'LICENSE'],
  'CSS': {
    _children: ['CSS-Note-1.md', 'CSS-Note-2.md', '性能優化.md'],
  },
  'Vue': {
    _children: ['性能優化.md', '新特性.md'],
    '無渲染Vue組件': {
      ...
    }
  }
}
複製代碼

每一個目錄層級下,對於單文件,直接存入這個層級下的一個名爲 _children的數組屬性中,對於文件夾,則將文件夾的名字做爲這個層級下一個屬性名,而後這個屬性的值,再按照上述規則進行遞歸,直到最後一層

固然,這個 Dir Tree的結構仁者見仁智者見智,只要你以爲順眼怎麼樣均可以,這裏只是舉了一個栗子◎

數據結構肯定了,下面就須要將文件路徑數組整理成上面的結構,例如,對於 /project/demo/src/index.js這個路徑,須要整理成:

const tree = {
  project: {
    demo: {
      src: {
        _children: ['index.js']
      }
    }
  }
}
複製代碼

那麼這裏就有個問題了,在創建這個 tree對象以前,tree這個數據多是沒有 project這個屬性的,又或者有 project屬性,可是這個屬性下沒有 demo這個屬性

解決的方法,很明顯的一個就是逐級判斷,沒有這個屬性的,就加上去,而後當構造出 tree.project.demo.src結構的時候,再在這個結構上,加上 ._children = ['index.js']結構,這個過程其實能夠簡化一下,好比藉助 元編程

例如,你要是以爲不要每次都要判斷到底有沒有這個屬性,那麼就可使用 es6 Proxy,實現自動添加屬性的能力:

function autoAddProperty() {
  return new Proxy({}, {
    get(target, key, receiver) {
      if (!(key in target)) {
          target[key] = autoAddProperty()
        }
        return Reflect.get(target, key, receiver)
    }
  })
}
// 用法
const obj = autoAddProperty()
obj.a.b.c.d = 1
console.log(obj.a.b.c.d)  // => 1
複製代碼

固然,除了 proxy以外,也能夠藉助 eval,本示例使用的就是 eval,由於代碼更簡潔,eval 這個方法可能不少 JavaScript書籍上都會提到不要隨便用,對於新手來講,這個特性坑比較多,因此在不明確其反作用的狀況下,仍是最好不要用,但並非說不能用,若是用這個特性多寫一行代碼就能另外少寫十行,那爲何不用一用?

更多關於元編程的內容,可見 知乎: 怎麼理解元編程?【資源集合】 ES6 元編程(Proxy & Reflect & Symbol)

輸出結構

數據結構搞定了,那麼最後的輸出就很簡單了,就是按照層級進行解構,一樣是須要用到尾遞歸,值得稍微提一下的就是,想讓輸出的目錄結構呈現出一種次序關係,那麼就須要在遞歸中記住層級關係,可經過指定一個參數 level來實現,根據這個參數的值來決定製表符 \t的數量,從而控制縮進來表現層級

function formatLink(obj = structs, basePath = '', level = 1) {
  // ...
}
複製代碼

另外,給你們說個小 tipgithub頁面上是可使用快捷鍵的,例如在一個 github倉庫頁面上按下 t鍵,就會激活查找文件模式,不一樣的頁面,例如帳戶我的主頁和某個倉庫頁面可用的快捷鍵可能全部差異,並且這些快捷鍵不少,想要憑記憶記下來可能有些困難,能夠按下 shift + /鍵,便可在當前 github頁面彈出一個 modal彈窗,上面就顯示當前頁面全部可用的快捷鍵

總結

實際上我作完這件事情後,最大的收穫並非寫出了一個小程序,解決了我想使用代碼解放雙手自動生成文件目錄樹的問題,而是在解決這個問題的過程當中,延伸開來學到的其餘的東西,例如尾遞歸、元編程乃至是 github的快捷鍵,這纔是價值更大的收貨。

這些東西都是我之前不知道的東西,而且由於這些知識可能對於小白不太友好因此很難在其餘的技術文章中看到,因此若是我不主動探尋,可能要再過很長一段時間甚至是永遠都不知道。

不懂不可怕,由於只要你想懂你總會懂的,但不知道就很可怕了,不知道就是不知道,哪怕那東西再簡單,但就由於你不知道因此你就是不知道,這就無解了

本文示例代碼已經放到 github上了,嗯,這個 github倉庫下 README.md上展現的目錄層級結構,就是根據這份文件生成的

相關文章
相關標籤/搜索