”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 创建一个CLI到脚手架扩展

创建一个CLI到脚手架扩展

发布于2025-02-26
浏览:706

在我们以前的练习中,我们使用Typescript构建了浏览器扩展。这涉及一系列步骤,包括创建一个Vite项目和自定义以满足浏览器扩展的特定要求。尽管该过程不是特别冗长或复杂,但我们可以通过使用节点CLI(命令行接口)自动化它来进一步简化它。如果您是CLI的新手,请让我介绍我创建的一个!

创建一个节点项目

自然,第一步是初始化并设置我们的节点项目。使用以下命令为我们的代码创建文件夹并生成基本软件包。

mkdir create-browser-extension-vite && cd create-browser-extension-vite NPM Init-是的
mkdir create-browser-extension-vite && cd create-browser-extension-vite
npm init --yes
接下来,我决定修改生成的软件包。json以包括“类型”:“模块”。有了这一点,我们将告知节点将项目中的.js文件解释为ES模块,而不是commonjs模块。这是更新的软件包。进行一些调整后。


{ “名称”:“ create-browser-extension-vite”, “版本”:“ 1.0.0”, “脚本”:{ “ test”:“ echo \”错误:未指定的测试\“ && exit 1” },, “ PublishConfig”:{ “访问”:“公共” },, “关键字”:[ “ CLI”, “创建项目” ],, “作者”: ””, “许可证”:“ ISC”, “描述”:“使用Vite创建浏览器扩展程序的CLI工具”, “类型”:“模块” }
mkdir create-browser-extension-vite && cd create-browser-extension-vite
npm init --yes
第一步

首先创建一个名为[

的新文件夹中创建一个名为 #!/usr/bin/env节点 console.log(“ Hello World”);
此文件将充当您的命令的入口点,并确保它可以在您的计算机上直接在计算机上直接ran

在全球安装后,将以下字段添加到package.json。这是给出的
#!/usr/bin/env node

console.log("hello world");
“ bin”:“ bin/create-project”

现在是时候测试我们到目前为止构建的内容了。首先,我们通过运行本地安装软件包:

NPM链接 create-browser-extension-vite //执行CLI
mkdir create-browser-extension-vite && cd create-browser-extension-vite
npm init --yes
链接后,您将拥有一个名为create-browser-extension-vite的新CLI命令,该命令目前仅将“ Hello World”打印到控制台。


,这就是创建基本CLI所需的一切!从这里,您可以利用节点生态系统的全部力量来构建您可以想象的任何东西。

处理用户输入
#!/usr/bin/env node

console.log("hello world");
让我们迈出迈向目标!该CLI的目的是用单个命令生成功能齐全的打字稿浏览器扩展程序。为此,CLI将接受几个可选参数。

[2

名称

:如果提供,将创建一个带有指定名称的文件夹。否则,当前文件夹将包含项目。

:如果指定,将为该项目初始化git存储库。

Create a CLI to scaffold extensions:如果指定,将自动安装项目依赖项。

  • :跳过所有提示并使用默认设置生成项目。
  • 的第一步是创建一个新文件SRC/CLI.JS,该文件将处理所有用于收集用户首选项的逻辑。该新模块将从当前
  • 调用。 #!/usr/bin/env节点 从“ ../src/cli.js”导入{cli}; CLI(Process.ARGV);
  • 要简化收集用户首选项的过程,我们将使用两个有用的库:
  • ARG:一个有力的参数解析器,用于处理命令行输入。
  • @询问器/提示:用于创建优雅和交互式命令行接口的库。

从“ arg”导入arg; 从“@询问器/提示”导入{escrence}; 异步函数提示formissingOptions(options){ if(options.skipprompts){ 返回选项; } 返回 { ...选项, git: options.git || (等待确认({消息:“初始化git存储库?”}),),), }; } 函数parseargumentsIntooptions(rawargs){ const args = arg( { “ - git”:布尔人, “ - 助手”:布尔, “ - 是的”:布尔, “ - 安装”:布尔, “ -g”:“ - git”, “ -y”:“ - 是的”, “ -i”:“ - 安装”, },, { argv:rawargs.slice(2), } ); 返回 { skipprompts:args [“ - 是”] ||错误的, git:args [“ - git”] ||错误的, runinstall:args [“ - install”] ||错误的, projectName:args ._ [0], }; } 导出异步函数cli(args){ 令options = parseargumentsIntooptions(args); 选项=等待提示formissingOptions(options); console.log(options); }
我会留给您添加一个额外的选项,以显示基本的帮助消息。这将涉及引入由-help或-h参数控制的新用户偏好。如果提供了此参数,则CLI应显示一个简单的手册,以说明命令的用法。您可以在下面链接的存储库中参考我的解决方案。

创建项目
#!/usr/bin/env node

console.log("hello world");
在此步骤中,将根据上一阶段选择的偏好创建项目。我们将首先创建一个名为“模板”的文件夹并将其复制到其中,将构成生成的项目的文件。

文件夹结构应该看起来像这样,您可以在我的github存储库中找到这些文件的内容。如果您对它们的创建方式感到好奇,请查看我以前的帖子,在这里我讨论使用Typescript构建浏览器扩展程序。 [2
我们的代码将利用模板文件夹中的文件来生成用户的新浏览器扩展程序,以下软件包将在实现这一目标时特别有用:

mkdir create-browser-extension-vite && cd create-browser-extension-vite
npm init --yes
    NCP:促进文件的递归复制。
  • 粉笔:添加终端字符串样式。
  • execa:简化运行的外部命令,例如git。
import arg from "arg";
import { confirm } from "@inquirer/prompts";

async function promptForMissingOptions(options) {
  if (options.skipPrompts) {
    return options;
  }

  return {
    ...options,
    git:
      options.git ||
      (await confirm({ message: "Initialize a git repository?" })),
  };
}

function parseArgumentsIntoOptions(rawArgs) {
  const args = arg(
    {
      "--git": Boolean,
      "--help": Boolean,
      "--yes": Boolean,
      "--install": Boolean,
      "-g": "--git",
      "-y": "--yes",
      "-i": "--install",
    },
    {
      argv: rawArgs.slice(2),
    }
  );

  return {
    skipPrompts: args["--yes"] || false,
    git: args["--git"] || false,
    runInstall: args["--install"] || false,
    projectName: args._[0],
  };
}

export async function cli(args) {
  let options = parseArgumentsIntoOptions(args);
  options = await promptForMissingOptions(options);
  console.log(options);
}

LIST:允许在为用户提供干净的进度概述时定义任务列表。

我们将首先创建一个新文件SRC/MAIN.JS,以包含通过从模板文件夹复制文件来生成项目的代码。

从“ ./main.js”导入{createProject}; ... 导出异步函数cli(args){ 令options = parseargumentsIntooptions(args); 选项=等待提示formissingOptions(options); 等待CreateProject(选项); }

从“粉笔”导入粉笔; 从“ NCP”导入NCP; 从“路径”导入路径; 从“ util”导入{promisify}; 从“ execa”导入{execa}; 从“ LIST”进口; 从“ pkg-install”导入{projectInstall}; const copy = promisify(ncp); 异步函数copyTemplateFiles(options){ 返回副本(options.templatedirectory,options.targetDirectory,{ Clobber:false, }); } 异步函数initgit(options){ const结果=等待execa(“ git”,[“ init”],{ CWD:options.targetDirectory, }); if(result.failed){ 返回Promise.Repject(新错误(“无法初始化Git”)); } 返回; } 导出异步函数createProject(options){ 选项= { ...选项, targetDirectory:options.projectName || process.cwd(), }; const currentfileurl = import.meta.url; const templatedir =路径。溶解( new URL(CurrentFileurl).pathname, “../../模板” ); options.templatedIrectory = templatedir; const任务= new LIST([ { 标题:“复制项目文件”, 任务:()=> copyTemplateFiles(options), },, { 标题:“初始化git”, 任务:()=> initgit(options), 已启用:()=> options.git, },, { 标题:“安装依赖项”, 任务:()=> ProjectInstall({ CWD:options.targetDirectory, }), skip :()=> !options.runinstall ? “通过 - 安装以自动安装依赖关系” : 不明确的, },, ); 等待Tasks.run(); console.log(“ ready%s project”,chalk.green.bold(“ done”)); 返回true; }

上面的代码利用LIST执行生成新项目所需的一系列操作,从使用NCP复制文件到设置GIT存储库。还请注意,我们如何使用Prosisify将NCP的基于回调的复制方法转换为基于承诺的功能,从而使代码更可读和可维护。 Create a CLI to scaffold extensions就是这样!这些是我遵循的步骤来创建新的CLI工具,这是我将用于简化新浏览器扩展名的创建的步骤。您也可以使用它!因为我已经在NPM上发布了它,以供任何人生成自己的扩展。

参考

如何用node.js
npm install ncp chalk execa pkg-install listr
    版本声明 本文转载于:https://dev.to/ivan_862363c9a8b0/create-a-cli-to-scaffold-extensions-145?1如有侵犯,请联系[email protected]删除
    最新教程 更多>
    • Go语言如何动态发现导出包类型?
      Go语言如何动态发现导出包类型?
      与反射软件包中的有限类型的发现能力相反,本文探索了替代方法,探索了在Runruntime。go import( “ FMT” “去/进口商” ) func main(){ pkg,err:= incorter.default()。导入(“ time”) 如果err...
      编程 发布于2025-05-10
    • 哪种在JavaScript中声明多个变量的方法更可维护?
      哪种在JavaScript中声明多个变量的方法更可维护?
      在JavaScript中声明多个变量:探索两个方法在JavaScript中,开发人员经常遇到需要声明多个变量的需要。对此的两种常见方法是:在单独的行上声明每个变量: 当涉及性能时,这两种方法本质上都是等效的。但是,可维护性可能会有所不同。 第一个方法被认为更易于维护。每个声明都是其自己的语句,使其...
      编程 发布于2025-05-10
    • 为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
      为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
      网格超过身体,用100%grid-template-columns 为什么在grid-template-colms中具有100%的显示器,当位置设置为设置的位置时,grid-template-colly修复了?问题: 考虑以下CSS和html: class =“ snippet-code”> g...
      编程 发布于2025-05-10
    • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
      如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
      postgresql:为每个唯一标识符在postgresql中提取最后一行,您可能需要遇到与数据集合中每个不同标识的信息相关的信息。考虑以下数据:[ 1 2014-02-01 kjkj 在数据集中的每个唯一ID中检索最后一行的信息,您可以在操作员上使用Postgres的有效效率: id dat...
      编程 发布于2025-05-10
    • 如何限制动态大小的父元素中元素的滚动范围?
      如何限制动态大小的父元素中元素的滚动范围?
      在交互式接口中实现垂直滚动元素的CSS高度限制问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期扩展,超过了视口的高度,阻止用户访问页面页脚。$("#map").css({ marginT...
      编程 发布于2025-05-10
    • 如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
      如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
      Transform Pandas DataFrame Column to DateTime FormatScenario:Data within a Pandas DataFrame often exists in various formats, including strings.使用时间数据时...
      编程 发布于2025-05-10
    • Go语言垃圾回收如何处理切片内存?
      Go语言垃圾回收如何处理切片内存?
      Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片时,了解垃圾收集行为至关重要,以避免潜在的内存泄...
      编程 发布于2025-05-10
    • Python不会对超范围子串切片报错的原因
      Python不会对超范围子串切片报错的原因
      在python中用索引切片范围:二重性和空序列索引单个元素不同,该元素会引起错误,切片在序列的边界之外没有。这种行为源于索引和切片之间的基本差异。索引一个序列,例如“示例” [3],返回一个项目。但是,切片序列(例如“示例” [3:4])返回项目的子序列。索引不存在的元素时,例如“示例” [9] ...
      编程 发布于2025-05-10
    • Java中如何使用观察者模式实现自定义事件?
      Java中如何使用观察者模式实现自定义事件?
      在Java 中创建自定义事件的自定义事件在许多编程场景中都是无关紧要的,使组件能够基于特定的触发器相互通信。本文旨在解决以下内容:问题语句我们如何在Java中实现自定义事件以促进基于特定事件的对象之间的交互,定义了管理订阅者的类界面。以下代码片段演示了如何使用观察者模式创建自定义事件: args)...
      编程 发布于2025-05-10
    • Python中何时用"try"而非"if"检测变量值?
      Python中何时用"try"而非"if"检测变量值?
      使用“ try“ vs.” if”来测试python 在python中的变量值,在某些情况下,您可能需要在处理之前检查变量是否具有值。在使用“如果”或“ try”构建体之间决定。“ if” constructs result = function() 如果结果: 对于结果: ...
      编程 发布于2025-05-10
    • 如何在Java的全屏独家模式下处理用户输入?
      如何在Java的全屏独家模式下处理用户输入?
      Handling User Input in Full Screen Exclusive Mode in JavaIntroductionWhen running a Java application in full screen exclusive mode, the usual event ha...
      编程 发布于2025-05-10
    • 在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
      在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
      在C中的显式删除 在C中的动态内存分配时,开发人员通常会想知道是否需要手动调用“ delete”操作员在heap-exprogal exit exit上。本文深入研究了这个主题。 在C主函数中,使用了动态分配变量(HEAP内存)的指针。当应用程序退出时,此内存是否会自动发布?通常,是。但是,即使在这...
      编程 发布于2025-05-10
    • 如何在鼠标单击时编程选择DIV中的所有文本?
      如何在鼠标单击时编程选择DIV中的所有文本?
      在鼠标上选择div文本单击带有文本内容,用户如何使用单个鼠标单击单击div中的整个文本?这允许用户轻松拖放所选的文本或直接复制它。 在单个鼠标上单击的div元素中选择文本,您可以使用以下Javascript函数: function selecttext(canduterid){ if(do...
      编程 发布于2025-05-10
    • 在Java中如何为PNG文件添加坐标轴和标签?
      在Java中如何为PNG文件添加坐标轴和标签?
      如何用java 在现有png映像中添加轴和标签的axes和labels如何注释png文件可能具有挑战性。与其尝试可能导致错误和不一致的修改,不如建议在图表创建过程中集成注释。使用JFReechArt import java.awt.color; 导入java.awt.eventqueue; 导入...
      编程 发布于2025-05-10
    • Python中嵌套函数与闭包的区别是什么
      Python中嵌套函数与闭包的区别是什么
      嵌套函数与python 在python中的嵌套函数不被考虑闭合,因为它们不符合以下要求:不访问局部范围scliables to incling scliables在封装范围外执行范围的局部范围。 make_printer(msg): DEF打印机(): 打印(味精) ...
      编程 发布于2025-05-10

    免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

    Copyright© 2022 湘ICP备2022001581号-3