2017-06-06 13 views
1

我正嘗試在Node中編寫內容manglement程序。我是一個老的Ruby/Perl/Shell手,多年以來,我似乎無法得到在這些語言中工作的簡單代碼,在Node中看起來類似,簡單。NodeJS:需要習慣用法:讀取dir中的文件,連接,轉換,編寫

任務:找到所有的*.md文件,閱讀它們(在ls順序),改造他們,並用頭註釋和頁腳註釋括號他們。這些文件按順序具有一些Markdown,它們在組裝和轉換時是合理的HTML文檔。這裏有一個shell執行:

echo '<!-- Generate at:' $(date) ' -->' $(ls *.md |xargs cat|markdown)'<!-- Copyright Mumble-demo Inc. -->'

產生所需的HTML:

<!-- Generate at: Tue Jun 6 08:25:59 EDT 2017 --> <h1>This is a Markdown File</h1> <h2>Heading 1</h2> <p>Inside of markdown we can create many interesting items</p> <ul> <li>such</li> <li>as</li> <li>lists</li> </ul><!-- Copyright Mumble-demo Inc. --> 

紅寶石同樣是合理的......

#!/usr/bin/env ruby 
require 'kramdown' 

HEADER = "<!-- Generated at #{Time.now} -->\n" 
FOOTER = "\n<!-- Copyright Mumble-demo Inc. -->" 

OUTPUT = File.open("./output", "w") 

results = Dir.glob("*.md").map { |f| File.open(f).readlines.join() }.reduce(:+) 
OUTPUT.print(HEADER, Kramdown::Document.new(results).to_html, FOOTER) 

但我想不出該怎麼辦這在節點中感覺良好的方式(TM)

那種感覺的方式錯誤(™)與同步接口:

const fs = require("fs") 
const marked = require("marked") 

const HEADER = `<!-- Generated at ${new Date()} -->\n` 
const FOOTER = `\n<!-- Copyright Mumble-demo Inc. -->` 

fs.readdir(".", (err, files) => { 
    if (err) throw err; 
    let markdownFiles = files.filter((f) => f.endsWith(".md")) 

    let content = markdownFiles.reduce((memo, fileName) => { 
    return memo + fs.readFileSync(fileName, 'utf8') 
    }, "") 

    let contentString = [HEADER, marked(content), FOOTER].reduce((m, i) => m + i, "") 
    fs.writeFileSync("derp", contentString); 
    console.log(contentString); 

}) 

的方式,感覺不錯,但我不能去上班(™)是:

  1. 構建讀取流
  2. 管他們降價變換流
  3. 打開輸出流和重定向轉化數據,將其

好消息是 - 這種方法的作用,直到它把標題註釋放在頂部和底部。他們生活在代碼中,而不是文件系統中,所以我不能將它們「添加」爲另一個文件,以便將其轉換爲輸出流。大多數方法會產生:頁眉,頁腳,流式數據

顯然,pipe()工作異步工作,頁腳打印在讀取+轉換工作完成之前觸發。我已經嘗試了可怕的(破碎的)Promise鏈,最終沒有奏效。

一種替代方法是將頁眉和頁腳轉換爲流(看起來很奇怪......)並將它們流入輸出流(看起來很奇怪)。

我已經把這幾個經驗豐富的開發人員用這個......難道我們在這裏錯過了一些常見的成語或者它是其實這很難在Node中簡單地完成這個任務嗎?

回答

1

思考:

  • 我的大多數shell腳本,我只是讀取文件的內容轉換爲字符串,同步進行。這種方法不能縮放,但通常不需要。而且絃樂一切都變得更加簡單。
  • 如果您執行異步操作:使用異步功能和util.promisify()
  • 長期來看,asynchronous iteration and async generators也會對這種情況有所幫助。
0

您可以通過同步執行程序nsynjs運行它「感覺良好的方式」。您的代碼同樣可以轉換該工作示例:

MD-cat.js:

var nsynjs = require('nsynjs'); 
var nsynFs = require('../wrappers/nodeFs'); // part of nsynjs package, needs to be added manually 

var synchronousCode = function(nsynFs) { 
    var HEADER = "<!-- Generated at "+new Date()+" -->\n"; 
    var FOOTER = "\n<!-- Copyright Mumble-demo Inc. -->"; 

    var files = nsynFs.readdir(nsynjsCtx, ".").data; 

    var content=""; 
    for(var i=0; i<files.length; i++) { 
     var file = files[i]; 
     if(file.endsWith('.md')) 
      content+=nsynFs.readFile(nsynjsCtx,file,"utf8").data; 
    } 
    nsynFs.writeFile(nsynjsCtx,"derp",HEADER+content+FOOTER); 
}; 

nsynjs.run(synchronousCode, {},nsynFs, function() { 
    console.log('synchronousCode done') 
}); 

即使它看起來同步,它不使用引擎蓋下的任何同步功能,因此也不會封鎖節點事件循環。

0

嘗試Gulp,這是當今最慣用的方式。

如果不能或不想要,使用Promise鏈,他們感覺像殼管。

#!/usr/bin/env node 
'use strict'; 

const Promise = require('bluebird'); 
const fs = Promise.promisifyAll(require('fs')); 
const path = require('path'); 
const marked = require('marked'); 

const HEADER = `<!-- Generated at ${(new Date()).toISOString()} -->`; 
const FOOTER = '<!-- Copyright Mumble-demo Inc. -->'; 

fs.readdirAsync(process.cwd()) 
    .map((fileName) => Promise.all([fileName, fs.statAsync(fileName)])) 
    .filter(([fileName, stat]) => stat.isFile() && path.extname(fileName) === '.md') 
    .call('sort', ([a], [b]) => a.localeCompare(b, 'en-US')) 
    .map(([mdFileName]) => fs.readFileAsync(mdFileName, 'utf8')) 
    .then((mdFiles) => { 
    let out = [HEADER, marked(mdFiles.join('\n')), FOOTER].join('').replace(/\n/g, ''); 
    console.log(out); 
    return fs.writeFileAsync('out.html', out); 
    }) 
    .catch((err) => { 
    console.error(err); 
    process.exit(1); 
    }); 

思考:

  • 不要寫在節點同步的代碼,你將永遠後悔。
  • 承諾鏈對此類任務最適合。
  • stat.isFile()和sort(),只是安全功能,在bash和Ruby示例中缺少。刪除它們可以保存兩行代碼。
  • 在大多數情況下,Date.prototype.toString()的使用應該被視爲一個bug,因爲輸出是不可預測的,它是平臺和特定於語言環境的。
  • 節點流是矯枉過正的,直到你處理大文件,這通常不是降價任務的情況。
  • Shell管道也不使用文件系統流並將所有內容加載到內存中。效率大致相同。