Choices.js 贡献开发指南:代码风格约束、完整测试体系与 Rollup 构建标志详解

发布时间:2026/10/15 12:05:39

Choices.js 贡献开发指南:代码风格约束、完整测试体系与 Rollup 构建标志详解 前端UI组件【免费下载链接】ChoicesA vanilla JS customisable select box/text input plugin ⚡️项目地址https://gitcode.com/gh_mirrors/ch/Choices点击查看免费下载本指南以仓库根目录的 CONTRIBUTING.md 为骨架面向希望向 Choices.js 提交代码的开发者你将掌握仓库约定的代码风格与 lint 要求、理解从单元测试Vitest到端到端测试Playwright的完整验证链路并深入拆解npm run js:watch -- --environment ...这类构建标志背后的 Rollup 编译机制最终能按规范提交可合入的 Pull Request。文中所有命令、配置与结论均与当前仓库Choices.js v11.2.4的实际源码一一对应。贡献规范总览以现有代码风格为准CONTRIBUTING.md 开篇即点明本项目的贡献准则In lieu of a formal styleguide, take care to maintain the existing coding style ensuring there are no linting errors. Add unit tests for any new or changed functionality.翻译过来就是仓库没有单独下发一份正式的 styleguide 文档贡献者必须以“维持现有代码风格、不产生任何 lint 错误”为第一原则同时任何新增或修改的功能都必须配套单元测试。这两条是进入后续所有开发、构建与测试流程的前提。“不产生 lint 错误”在仓库中有实际的落地工具链支撑见 package.jsonnpm run lint并行执行lint:js与lint:scssnpm run lint:jseslint src/scripts test/scripts test-e2e即对源码、单元测试与 e2e 测试三块代码统一做 ESLint 检查npm run lint:scssstylelint src/**/*.scss对 src/styles 下的 SCSS 源文件做样式规范检查。此外仓库通过 scripts/lint-staged.config.js 在提交阶段强制这些检查*.js文件提交前执行eslint --fix --quiet自动修复并校验*.{ts,scss,yaml,yml,md,html,json,...}提交前统一走prettier --write格式化对src/scripts/**/*.js还会额外触发npm run test:unit确保修改的脚本不破坏既有单元测试。也就是说风格规范不是“建议”而是通过 ESLint Prettier Stylelint 三重关卡在每次提交时强制执行的工程约束。NPM 任务全览开发、测试与构建命令CONTRIBUTING.md 用一张表格列出了贡献者最常用的 npm 任务下表完整保留原始说明并补充了各任务在 package.json 中的真实底层实现TaskUsage原始说明底层实现package.jsonnpm run startFire up local server for developmentrun-p js:watch css:watch并行启动 JS 与 SCSS 监听npm run test:unitRun sequence of tests oncevitest runnpm run test:unit:watchFire up test server and re-test on file changenpm run test:unit -- --watch --inspect5556npm run test:e2eRun sequence of e2e tests (with local server)run-s playwright:cli串行执行 Playwright CLI 测试npm run testRun both unit and e2e testsrun-s test:unit test:e2e:allnpm run playwright:guiRun Playwright e2e tests (GUI)playwright test --ui --projectchromiumnpm run playwright:cliRun Playwright e2e tests (CLI)playwright test --projectchromiumnpm run js:buildCompile Choices to an uglified JavaScript filerollup ... --environment WITH_D_TS_FILES:1 mv public/assets/scripts/src public/types/npm run css:watchWatch SCSS files for changes. On a change, run build processnodemon -e scss -x npm run css:buildnpm run css:buildCompile, minify and prefix SCSS files to CSSrun-s css:sass css:prefix css:min几点值得注意的细节start是开发模式的入口。它同时拉起两条 watch 链路js:watch使用 Rollup 的-w参数监听源码变更并重新打包css:watch使用 nodemon 监听scss文件变化。e2e 测试也依赖它——playwright.config.ts 中的webServer.command正是npm run start默认访问http://localhost:3001。CSS 构建是三条流水线css:sass用 sass 将 src/styles/base.scss 与 src/styles/choices.scss 编译为 public/assets/styles 下的 CSScss:prefix用 postcss autoprefixer 补齐浏览器前缀css:min用 csso 产出base.min.css与choices.min.css。js:build不只是压缩 JS。它通过WITH_D_TS_FILES:1让 Rollup 在打包的同时生成 TypeScript 声明文件declaration: true见 scripts/rollup.config.mjs随后把产物移入public/types/——这就是 package.json 中types: ./public/types/src/index.d.ts的来源。向 Rollup 传递环境参数--语法与同名覆盖规则CONTRIBUTING.md 特别强调通过--后跟常规的 rollup--environment参数来传值同名参数以最后一个为准The last one overrides any previous ones with the same name。npm run js:watch -- --environment WATCH_HOST:0.0.0.0这条命令把WATCH_HOST设为0.0.0.0使开发服务器监听所有网卡而非默认的localhost方便局域网内的浏览器访问。其读取端在 scripts/rollup.config.mjs 与 scripts/server.mjsRollup 配置通过process.env.WATCH_HOST读取该变量scripts/server.mjs 中的server()函数只有在WATCH_HOST存在时才注入rollup-plugin-dev开发服务器端口默认WATCH_PORT || 3001目录指向publicplaywright.config.ts 中 e2e 测试的baseURL也正是http://localhost:3001/与之一致。这一套“npm 传参 → 环境变量 → Rollup 读取”的机制是后面所有构建标志Build Flags生效的基础。构建标志Build Flags三种搜索能力与 DOM 支持的编译期开关构建标志是 Choices.js 贡献体系中最核心的机制。CONTRIBUTING.md 定义了三个可通过环境变量控制的编译期开关其默认值与源码中的消费逻辑如下。CHOICES_SEARCH_FUSEfull/basic/nullUsageThe level of integration with fuse.fullis the entire fuse.js build,basicis fuse.js with just standard fuzzy searching.nullis a basic prefix string search with no fuse.js.它控制与 fuse.js 的集成深度full完整 fuse.js 构建默认值basic仅 fuse.js 的标准模糊搜索null完全不引入 fuse.js退化为最轻量的前缀字符串搜索。对应源码证据非常清晰。在 src/scripts/interfaces/build-flags.ts 中searchFuse直接读取process.env.CHOICES_SEARCH_FUSEsrc/scripts/search/index.ts 的getSearcher根据标志选择实现类export function getSearcherT extends object(config: Options): SearcherT { if (searchFuse !searchKMP) { return new SearchByFuseT(config); // full / basic } if (searchKMP) { return new SearchByKMPT(config); } return new SearchByPrefixFilterT(config); // null }full与basic都走 src/scripts/search/fuse.ts前者import FuseFull from fuse.js后者import FuseBasic from fuse.js/basic二者共用同一个SearchByFuse类按searchFuse full选择实例化哪个 fuse 版本null走 src/scripts/search/prefix-filter.ts 的SearchByPrefixFilter它对searchFields中的每个字段做toLowerCase().startsWith(needle)前缀匹配完全不依赖 fuse.js。用法示例来自 CONTRIBUTING.mdnpm run js:watch -- --environment CHOICES_SEARCH_FUSE:basicCHOICES_SEARCH_KMP1/0UsageHigh performanceindexOf-like search algorithm.开启后使用一个高性能的、类似indexOf的 KMPKnuth–Morris–Pratt字符串匹配算法。标志的读取同样在 src/scripts/interfaces/build-flags.tssearchKMP process.env.CHOICES_SEARCH_KMP 1。其实现位于 src/scripts/search/kmp.ts先为搜索模式串计算“最长公共前后缀表”LSP 表kmpSearch的前半部分再扫描文本失配时按 LSP 表回退j lsp[j - 1]从而把匹配复杂度控制在 O(n) 量级避免朴素算法的重复比较对每个 choice 的每个searchFields字段执行该匹配命中即返回结果。注意一个优先级细节在getSearcher中searchKMP的优先级高于 fuse——只要CHOICES_SEARCH_KMP1即使 fuse 标志为full也会选择 KMP 实现。npm run js:watch -- --environment CHOICES_SEARCH_KMP:1CHOICES_CAN_USE_DOM1/0UsageIndicates if DOM methods are supported in the global namespace. Useful if importing into DOM or the e2e tests without a DOM implementation available.该标志声明全局命名空间是否具备 DOM 能力。在 src/scripts/interfaces/build-flags.ts 中export const canUseDom: boolean process.env.CHOICES_CAN_USE_DOM ! undefined ? process.env.CHOICES_CAN_USE_DOM 1 : !!(typeof document ! undefined document.createElement);显式传入1/0时以显式值为准未传入时回退到运行时探测检查document是否存在且具备createElement。这对“在无 DOM 环境下导入库”或“e2e 测试中注入构建产物”很有价值——通过显式置0可以让库在 Node 等无 DOM 环境里安全加载。npm run js:watch -- --environment CHOICES_CAN_USE_DOM:1标志背后的编译机制rollup/plugin-replace与 tree-shaking构建标志不只是“配置项”它们在编译期被真实替换从而影响产物体积。在 scripts/rollup.config.mjs 中const buildFeatures { CHOICES_SEARCH_FUSE: full, // basic / null CHOICES_SEARCH_KMP: 0, // 1 CHOICES_CAN_USE_DOM: 1, // 0 }每个环境变量都被replace插件注入为process.env.X常量值为null时会被转换为falseRollup 据此在打包阶段做 tree-shaking——未启用的搜索实现fuse / KMP / prefix整段代码都不会进入最终产物。这正是 public/assets/scripts 下同时存在choices.js、choices.search-basic.js、choices.search-prefix.js、choices.search-kmp.js等变体的原因也对应 package.json 中./search-basic、./search-prefix、./search-kmp、./search-none的 exports 入口——使用者可以按体积与搜索能力需求选择对应构建。值得留意的是 src/scripts/interfaces/build-flags.ts 末尾的注释These are not directly used, as an exported object (even as const) will prevent tree-shake away code paths即这些标志变量必须被单独、直接地导出而不是聚合在一个导出对象里否则会阻碍 tree-shaking 把死代码摇掉——这是写构建相关源码时必须遵守的纪律。测试驱动贡献从 Vitest 单元测试到 Playwright 端到端测试单元测试npm run test:unit单元测试由 Vitest 驱动配置见 vitest.config.tsenvironment: jsdom在 jsdom 模拟的 DOM 环境里运行setupFiles: [./test/setupFiles/window-matchMedia.ts]启动前注入window.matchMedia的 mock见 test/setupFiles/window-matchMedia.tsinclude: [test/**/*.{test,spec}.?(c|m)[jt]s?(x)]测试文件统一放在 test/scripts 下与 src/scripts 的目录结构一一对应components、reducers、actions、store、search 等。贡献者提交新功能时应模仿现有测试的写法。以搜索模块为例test/scripts/search/index.test.ts 对SearchByFusefull / basic 两档、SearchByKMP、SearchByPrefixFilter四套实现做了等价性验证空输入返回空结果、前缀命中数量、后缀匹配行为、结果顺序等。注意其中prefix-filter场景下搜索label 9的“后缀”用例期望返回 0 条——因为前缀过滤器只做startsWith这正好印证了不同构建标志在行为上的差异。模板系统的测试在 test/scripts/templates.test.ts它通过shimTemplates将 src/scripts/templates.ts 中的默认模板方法绑定到伪造的 Choices 实例上再逐一断言每个模板产出的 DOM 结构、data 属性与 class。端到端测试npm run test:e2ee2e 测试基于 Playwright配置见 playwright.config.ts测试目录为./test-e2e覆盖 select-one、select-multiple、text、性能与 demo 页面如 test-e2e/tests/select-multiple-performance.spec.ts内置 chromium / firefox / webkit 三个浏览器项目测试前由webServer自动执行npm run start拉起本地服务端口 3001通过 test-e2e/bundle-test.ts 的bundlefixture 注入被测脚本地址CI 下用choices.min.js本地开发用未压缩的choices.js确保压缩与未压缩产物都被真实浏览器验证截图快照存放在 test-e2e/screenshots命名模板为{projectName}-{platform}{ext}。压缩产物的纯度约束new与get必须无副作用CONTRIBUTING.md 有一行容易忽略但极其重要的硬性要求For compatibility,newandgetmust be pure (side effect free).这行约束与压缩流程直接相关。在 scripts/rollup.config.mjs 中非 ESM 输出会经过 terser 压缩且压缩参数显式开启了terser({ compress: { passes: 2, pure_getters: true, pure_new: true, } })pure_getters: trueterser 假定读取 getter 没有副作用从而可以安全删除“只读不用”的访问表达式pure_new: trueterser 假定执行new构造没有副作用允许在结果未被使用时整段丢弃。因此如果贡献者编写的代码里getter 或构造函数带有隐式副作用例如在 getter 中修改外部状态、在构造时触发 DOM 写入压缩后的 min 产物就会与开发产物行为不一致——这正是“new和get必须无副作用”这条约定存在的根本原因。写代码时请保持构造函数与属性 getter 的纯粹性避免在压缩后“翻车”。提交 Pull Request使用问题模板最后CONTRIBUTING.md 建议当提交的 Pull Request 用于修复 bug 时尽量使用以下三段式模板帮助维护者快速复现与评审## This is the problem: ## Steps to reproduce: ## This is my solution:即描述问题 → 给出可复现步骤 → 说明你的解决方案。配合前文的 lint 检查、单元测试与构建标志验证一份合格的 PR 应当做到无 lint 错误、新功能有配套单测、改动逻辑尤其涉及搜索构建时在对应标志组合下验证通过且不违反new/get无副作用的纯度约束。小结Choices.js 的贡献流程可以浓缩为四条主线风格上以现有代码为准并通过 ESLint/Prettier/Stylelint 强制把关功能上任何改动必须伴随单元测试并经过 Vitest Playwright 双层验证构建上通过-- --environment传入的三个构建标志在编译期裁剪搜索能力与 DOM 依赖代码质量上保证new与get无副作用以兼容压缩优化。理解了这些机制你既能为这个轻量级选择框插件贡献高质量代码也能在需要时基于这套“多构建变体 tree-shaking”模式为自己的库设计类似的可插拔编译体系。赞分享前端UI组件【免费下载链接】ChoicesA vanilla JS customisable select box/text input plugin ⚡️项目地址https://gitcode.com/gh_mirrors/ch/Choices点击查看免费下载相关推荐开发者指南Valentina测试体系、代码风格与开源贡献流程开发者指南Valentina测试体系、代码风格与开源贡献流程 Valentinafashionmaker是一款跨平台服装打版程序本文带你完整了解它的开发桌面应用图形学Kong 开源贡献指南PR 规范、Commit 约定、测试体系与 Lua 代码风格Kong 开源贡献指南PR 规范、Commit 约定、测试体系与 Lua 代码风格 本文基于 Kong 仓库根目录的 CONTRIBUTING.md httpAPI网关后端LLM 网关微服务人工智能Tornado 源码贡献指南从测试、代码风格到文档构建的完整开发工作流Tornado 源码贡献指南从测试、代码风格到文档构建的完整开发工作流 Tornado 是源自 FriendFeed 的 Python Web 框架与异步网络后端Web框架异步编程WebSocket创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/15 12:00:39

Python爬虫实战:requests+BeautifulSoup+lxml抓取静态网页数据

1. 从"语法都会、项目不会"说起:为什么拿静态网页练手是最优解学完Python基础语法之后的那段空窗期,几乎是每个自学者都会经历的。变量、循环、函数、类都过了一遍,练习题也刷了不少,可一旦合上教程,面对一个…

2026/10/15 12:00:39

光伏并网验收测试:基于仿真网关的DREAMS对接验证实践

简介:面向光伏监控系统测试人员、运维人员及需要对接台电DREAMS规范的云平台厂商,这份测试工具包用于验证云端太阳能监控系统是否符合台电DREAMS要求。核心围绕DNP 3.0协议实现发电数据回传与控制指令交互,可测试变流器实功、虚功、功率因数上…

2026/10/15 12:00:39

DL/T 698.45规约源代码实战:链路层、OAD与主站收发骨架

简介:这是一份面向电力行业采集系统开发者的 DL/T 698.45 规约源代码,专注解决主站、采集终端及电能表之间的互操作性数据交换需求。资源涵盖协议完整实现,包括通信架构、数据链路层、应用层、接口类与对象标识等模块,适合从事 69…

2026/10/15 18:19:15

Qt+CMake三大配置范式:从Demo到企业级项目的演进路径

1. 为什么Qt项目里CMakeLists.txt总让人“改得心虚”?你有没有过这种经历:刚用Qt Creator新建一个Qt Widgets Application,点开CMakeLists.txt一看,满屏的find_package(Qt6 REQUIRED COMPONENTS Widgets)、target_link_libraries(…

2026/10/15 18:19:15

吃透六自由度机器人D-H法:建模、正逆解与避坑全攻略

简介:六自由度机器人D-H法学习资料共3个文件,压缩包仅2.27MB,包含2个MATLAB程序(m文件)和1份Word说明文档。资料面向机器人学初学者、高校相关课程学生以及需要完成运动学作业的工程师,系统讲解D-H参数法原…

2026/10/15 18:19:15

Python调用Simulink编译DLL:零MATLAB依赖的轻量部署方案

简介:本资源是一套面向Python开发者与嵌入式仿真工程师的Simulink模型加速实践方案,聚焦于将Simulink模型编译为DLL并在Python中高效调用,显著降低MIL(模型在环)测试对MATLAB/Simulink许可证和运行时环境的依赖。资源以…

2026/10/15 18:19:15

流程控制语句精讲:选择与循环的工程实践

1. 流程控制语句的核心分类与设计思路 代码是自上而下一条一条读的,但真实的业务场景很少会这么听话。登录时要对账号密码做校验,匹配不到就走错误分支;列表里要一条一条翻数据,翻到满足条件的记录就提前收手;游戏里按…

2026/10/15 18:19:15

STM8外部中断寄存器配置与抗干扰实战指南

简介:本资源是一份面向STM8嵌入式开发初学者与进阶工程师的外部中断实战代码包,聚焦IAR环境下中断配置、触发响应与服务函数编写等核心能力训练,解决实时事件响应难、中断逻辑调试易出错等典型问题。压缩包共211个文件,以64个.h头…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/15 0:02:22

防关联浏览器哪个好?三款产品实测差异解析

新手做多店铺,最纠结的就是防关联浏览器选哪个。飞跨、网易、站斧这三款被问得最多,先给结论:要一体化省心、长期便宜,选站斧;要网易招牌和稳,选网易;预算紧做东南亚 Lazada,选飞跨。…

2026/10/15 0:02:22

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成 一、技术背景 大模型(LLM)在通用场景下表现强大,但当我们将 GPT-4 或 Qwen 直接用于生成影刀RPA操作指令和 XPath 定位时,准确率通常只有 40%-60%。原因…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑