Script insert comment vào đầu mỗi file trong project bằng Nodejs
Bài đăng này đã không được cập nhật trong 5 năm
Tại sao lại có bài viết này?
Đó là cả một câu chuyện mọi người ạ. Mình xin được phép tóm tắt lại như sau:
Câu chuyện được mang tên "Sếp tổng và em coder xinh xắn"
Vào một ngày đẹp trời nọ, trong lúc đang làm dự án "Về nhà đi con" ngon lành cành đào thì team mình được đón tiếp một em hot girl coder xinh xắn mới vào. Anh em náo nức chưa được bao lâu thì nhận được một chỉ thị từ sếp tổng sư huynh:
"Xin chào mọi người, bắt đầu từ hôm nay, mọi người hãy add đoạn code này vào tất cả các file trong dự án nhé "
Đoạn code có dạng như sau:
// Day_la_du_an_cua_sep_***_dep_zai_ahihi
Các bạn thấy có bê đê biến thái ko chứ. Nhưng thôi kệ, nhận nhiệm vụ rồi thì phải làm thôi. Đó cũng là lý do xuất hiện bài viết này.
Mình đùa thôi chứ câu chuyện chỉ có 10% sự thật thôi nhé :v
OK, Let's get it !!
Mục tiêu
Mục tiêu: vì các project của team mình chủ yếu là frontend nên mình sẽ thử tạo một tool bằng javascript, cụ thể hơn là mình sẽ sử dụng NodeJS để đọc và viết vào file. Sau đó sẽ add một lệnh kiểu như npm run gencomment
vào package.json để thực thi đoạn code.
Khởi tạo thư mục, file
Ở bài viết này, mình sẽ create-react-app
một project để có một vài file để test. Các bạn cũng có thể npm init
rồi tạo một vài file bất kỳ gì cũng được.
$ npx create-react-app gencomment
Tiếp theo chúng ta sẽ tạo 1 file gencomment.js và thêm script vào file package.json để test 1 xíu trước khi vào code nhé. Thành quả ban đầu:
├── public/
├── src/
├── gencomment.js
├── package.json
//gencomment.js
console.log('BEGIN');
//package.json
{
"script": {
"gencomment": "node gencomment.js"
}
}
Chúng ta hãy run npm run gencomment
để đảm bảo code chạy được trước khi qua phần tiếp theo nhé. Kết quả mong muốn là sẽ log ra được message 'BEGIN' mọi người nhé :v
Code
OK, vào phần chính thôi nào.
Thuật toán mình nghĩ cũng đơn giản thôi:
- Đầu tiên chúng ta sẽ đọc folder để lặp qua các file và folder con (sử dụng thư viện
fs
vàpath
có sẵn) - Tiếp đến là check xem đó là một file hay là folder, nếu folder thì lặp lại Bước 1
- Nếu là file thì đọc dòng đầu tiên của file xem có comment chưa .Nếu chưa thì insert comment vào. Ở bước này, để chỉ đọc dòng đầu tiên của file mà không cần phải đọc toàn bộ file mình sẽ sử dụng package
firstline
. Package này dùng read stream khá là nhanh, và chỉ có 30 dòng code thôi, mọi người có thể xem qua nhé.
Sau một hồi search google, xem document các thứ thì đây là phần code của mình:
const fs = require("fs")
const path = require("path")
const firstline = require("firstline")
// just allow js and jsx file for this demo
const config = {
COMMENT: "// Day_la_du_an_cua_sep_***_dep_zai_ahihi",
ALLOW_FILE_EXT: ["js", "jsx"],
}
/**
* This function will insert comment in every file with ALLOW_FILE_EXT.
* @param {String} dir A path to directory
*/
const genComment = dir => {
// loop through folder
fs.readdirSync(dir).map(view => {
// if is folder: genComment folder
if (view.split(".")[1] === undefined) {
genComment(path.resolve(dir, view))
}
// else check if is allowed file
else if (config.ALLOW_FILE_EXT.indexOf(view.split(".").pop()) >= 0) {
const filePath = path.resolve(dir, view)
// check if file already has comment
firstline(filePath).then(firstLineContent => {
if (!firstLineContent.includes(config.COMMENT)) {
// store current content
const fileCurrentContent = fs.readFileSync(filePath)
// write comment
fs.writeFileSync(filePath, `${config.COMMENT}\n`)
// append current content
fs.appendFile(filePath, fileCurrentContent, err => {
if (err) throw err
})
}
})
}
})
}
genComment(path.resolve(__dirname, "src"))
Vậy trường hợp chúng ta muốn xóa tất cả comment vừa insert vào thì sao :-?
Đối với trường hợp này chúng ta sẽ có 2 cách, một là tạo thêm một file removecomment.js xong rồi add thêm 1 script npm removecomment
nữa hoặc là mở rộng script hiện tại sao cho nó có thể nhận tham số add hoặc remove kiểu như sau:
$ npm run gencomment add "some comment"
$ npm run gencomment remove "some comment"
Cách thứ 2 có vẻ OK hơn nhỉ, vậy chúng ta sẽ làm theo cách này xem nhé.
Đối với trường hợp này, mình sẽ sử dụng process.argv
để parse command line argument.
Kết quả
Toàn bộ code của chúng ta sẽ như sau, mình có update lại 1 xíu nhé :v
const fs = require("fs");
const path = require("path");
const firstline = require("firstline");
const config = {
COMMENT_PREFIX: "//",
ALLOW_FILE_EXT: ["js", "jsx"],
};
const MODE = {
ADD: "add",
REMOVE: "remove",
};
/**
* This function will insert comment in every file with ALLOW_FILE_EXT.
* @param {String} dir A path to directory
*/
const genComment = ({ dir, mode, comment }) => {
const _comment = `${config.COMMENT_PREFIX} ${comment}`;
fs.readdirSync(dir).map(view => {
// if is folder: genComment folder
if (view.split(".")[1] === undefined) {
genComment({ dir: path.resolve(dir, view), mode, comment });
}
// else check if is allowed file
else if (config.ALLOW_FILE_EXT.indexOf(view.split(".").pop()) >= 0) {
const filePath = path.resolve(dir, view);
// check if file already has comment
firstline(filePath).then(firstLineContent => {
if (mode === MODE.ADD) {
if (!firstLineContent.includes(_comment)) {
// store current content
const fileCurrentContent = fs.readFileSync(filePath);
// write comment
fs.writeFileSync(filePath, `${_comment}\n`);
// append current content
fs.appendFile(filePath, fileCurrentContent, err => {
if (err) throw err;
});
}
} else {
if (firstLineContent.includes(_comment)) {
// read file
fs.readFile(filePath, "utf8", (err, data) => {
if (err) throw err;
// write all content except first line
fs.writeFileSync(
filePath,
data
.split("\n")
.slice(1)
.join("\n"),
);
});
}
}
});
}
});
};
const mode = process.argv[2];
const comment = process.argv[3];
// Check if command line argument is valid
if (Object.values(MODE).includes(mode) && comment) {
genComment({
dir: path.resolve(__dirname, "src"),
mode,
comment,
});
}
Chúc mọi người thành công !!
All rights reserved