1.0.1 • Published 2 years ago

juejin-save v1.0.1

Weekly downloads
-
License
AGPL-3.0 License
Repository
github
Last release
2 years ago

打造一个保存掘金文章的 cli

安装

npm i juejin-save -g

使用

juejin-save save https://xxx

效果

gif


1. 主要 package version

注意:文章重点在于打造 cli,不是 puppeteer

packageversion功能
commander^9.0.0创建处理命令
inquirer^8.2.0处理交互
ora^5.4.1处理 loading
puppeteer^13.3.2通过 api 来控制 Chromium 或 Chrome

2. 项目目录结构

├── bin
|  └── cli.js          // 入口文件
├── LICENSE
├── package-lock.json
├── package.json
├── puppeteer.js       // puppeteer保存文章文件
└── README.md

2. package.json 中添加bin字段

添加juejin-save命令,指定运行文件为 bin 目录 cli.js

+ {
+   "bin": {
+     "juejin-save": "bin/cli.js"
+   }
+ }

3. 主要 package api 介绍

3.1 commander

const { Command } = require("commander");
const program = new Command();

program
  .command("clone <source> [destination]")
  .description("clone a repository into a newly created directory")
  .action((source, destination) => {
    console.log("clone command called");
  });

program.parse();

3.2 inquirer

var inquirer = require("inquirer");
inquirer
  .prompt([
    /* Pass your questions in here */
  ])
  .then((answers) => {
    // Use user feedback for... whatever!!
  })
  .catch((error) => {
    if (error.isTtyError) {
      // Prompt couldn't be rendered in the current environment
    } else {
      // Something else went wrong
    }
  });

3.3 ora

import ora from "ora";

const spinner = ora("Loading unicorns").start();

setTimeout(() => {
  spinner.color = "yellow";
  spinner.text = "Loading rainbows";
}, 1000);

3.4 puppeteer

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto("https://example.com");
  await page.screenshot({ path: "example.png" });

  await browser.close();
})();

4. 主要逻辑代码

  1. bin/cli.js
#!/usr/bin/env node

const inquirer = require("inquirer");
const ora = require("ora");
const { Command } = require("commander");
const { puppeteerInit, saveToHtml, saveToMd, saveToPdf } = require(path.resolve(
  __dirname,
  "../puppeteer"
));

const program = new Command();
const spinner = ora();

// 交互式询问
async function handlePrompt() {
  return await inquirer.prompt([
    {
      name: "autoCreateFolder",
      message: `Automatically create folders?`,
      type: "confirm",
    },
    //...
  ]);
}

// 询问过后的处理,开始puppeteer初始化
async function AfterePrompt(articleUrl, answers) {
  spinner.color = "yellow";
  spinner.start("puppeteer intial...");

  const obj = await puppeteerInit(articleUrl, answers);

  spinner.stopAndPersist({
    symbol: chalk.green("✓"),
    text: chalk.green("puppeteer init ok"),
  });

  return obj;
}

// 导出文件
async function exportFile(arg) {
  const { page, outMdFilePath, outPdfFilePath, outHtmlfFilePath } = arg;
  await saveToMd(page, outMdFilePath);
  await saveToPdf(page, outPdfFilePath);
  await saveToHtml(page, outHtmlfFilePath);
}

// 第一步:创建命令
program
  .version(require(path.resolve(__dirname, "../package.json")).version)
  .command("save  <article-url>")
  .description("save https://xxx")
  .action(async (articleUrl) => {
    // 第二步:交互式询问
    const answers = await handlePrompt(articleUrl);
    // 第三步:拿到交互结果
    const data = await AfterePrompt(articleUrl, answers);
    // 第四步:导出文件
    await exportFile(data);

    process.exit(1);
  });

program.parse();
  1. puppeteer.js
const puppeteer = require("puppeteer");

// 保存html
async function saveToHtml(page, outHtmlfFilePath) {
  // ...
}

// 保存markdown
async function saveToMd(page, outMdFilePath) {
  // ...
}

// 保存pdf
async function saveToPdf(page, outPdfFilePath) {
  // ...
}

// puppeteer初始化
async function puppeteerInit(href) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  page.setViewport({
    width: 1920,
    height: 1080,
  });

  await page.goto(href, {
    waitUntil: "domcontentloaded",
    referer: href,
  });
  await page.waitForTimeout(3000); // 确保页面加载完毕

  return {
    browser,
    page,
  };
}

module.exports = {
  puppeteerInit,
  saveToHtml,
  saveToMd,
  saveToPdf,
};

5. 本地测试

  1. 在项目根目录执行
npm link

执行完之后,成功提示:

added 1 package, and audited 3 packages in 1s

found 0 vulnerabilities

也可以在本机的 npm 全局安装里找到一个软链接,如图:

npm.io

  1. 在任意目录打开命令行,执行
juejin-save save  https://juejin.cn/post/xxxx

不出意外,可以看到,多出了一个文件夹,文章被保存在文件夹里面了。

6. 参考资料

  1. 手写一个合格的前端脚手架

  2. 实现 CLI 常用工具包 - 终端交互相关