
开发工具代码质量静态分析【免费下载链接】jscpdCopy/paste detector for source code. 220 languages, Rust engine, SARIF/HTML/badge reporters, GitHub Action, MCP server for AI agents.项目地址https://gitcode.com/gh_mirrors/js/jscpd点击查看免费下载Vue、Svelte、Astro 这类单文件组件Single-File ComponentSFC把模板、脚本、样式写进同一个文件传统的整文件按一种语言扫描策略无法处理。本文以 jscpd 仓库的 fixtures/sfc-demo 演示集为线索完整讲解 jscpd 如何把.vue、.svelte、.astro文件拆分成独立块block让每个块按自己的语言参与克隆检测并在报告中用Card.vue:html这样的文件名:块语言格式精确指出克隆发生的位置。读完本文你将掌握 SFC 场景下克隆报告的解读方法、template-range演示的验证步骤以及 wrapper 标签为何被排除在 HTML 流之外的底层原理。一、核心思路文件拆块块内按语言扫描jscpd 不把 Vue/Svelte/Astro 文件当作单一语言整体扫描而是先按顶层结构把文件拆成块再为每一块选择对应语言script块体 → 按 JavaScript 或 TypeScript 扫描取决于lang属性style块体 → 按 CSS、SCSS 或 Less 扫描取决于lang属性标记markup部分 → 按 HTML 扫描Vue 中是template块体Svelte/Astro 中是顶层标记。这一策略的直接结果是克隆发生在哪个块内就以哪个块的语言上报。文件内多个块的 token 分别进入各自的流stream互不混淆因此一个共享模板不会因为脚本或样式不同而被误判为整文件重复脚本与样式部分也不会被算进重复行数。在源码层面这一逻辑位于 rust/crates/cpd-tokenizer/src/sfc.rs 的tokenize_sfc_maps第 26 行起它先通过find_sfc_blocks找到文件中的块把块的内部区间inner_start..inner_end作为空白范围做脱敏处理再对脱敏后的骨架按html做通用 token 化对每个块体按其block_format分别 token 化最终按格式分组输出多个TokenMap。源码注释与测试明确印证了三块模型vue_file_extracts_three_blocks断言一个标准 Vue 文件必须找到template、script、style三个块vue_sfc_maps_produces_multiple_formats断言产出结果同时包含javascript、css、html三种格式的映射。块格式由属性决定块体选用哪种语言并不是写死的而是由开标签属性探测得出对应 sfc.rs 中的detect_block_format第 215 行起标签lang属性块格式script无javascriptscriptts/typescripttypescriptscriptjs/javascriptjavascriptscript其他已注册格式该格式名未知则回落javascriptstyle无cssstylescss/sassscssstylelesslesstemplatepug/jadepug否则html仓库测试script_lang_ts_produces_typescript_format与vue_ts_maps_produces_typescript分别验证了script langts产出typescript块、style langscss产出scss块。需要说明的是这些格式同样注册在 rust/crates/cpd-tokenizer/src/formats.rs 的SUPPORTED_FORMATS表中vue扩展名vue、svelte扩展名svelte、astro扩展名astro都是正式格式条目。二、克隆报告如何指出块文件名:块语言因为同一文件可能同时存在 HTML、JS、CSS 三条流jscpd 控制台报告在克隆行首用文件名:块语言区分克隆发生在哪个块# Clone found (html) # - Card.vue:html [2:3 - 15:13] (14 lines, 156 tokens)格式解读Card.vue—— 文件路径:html—— 克隆位于该文件的 HTML 块中同理可能出现:javascript、:typescript、:css、:scss、:less等后缀[2:3 - 15:13]—— 克隆在文件中的起止位置第 2 行第 3 列到第 15 行第 13 列(14 lines, 156 tokens)—— 重复块的行数与 token 数。这意味着克隆边界永远落在块内部绝不会从template一直拖到/style脚本和样式行也不会被计入重复行数。三、实战演示template-range/模板共享、脚本与样式各不同演示目录 fixtures/sfc-demo/template-range 包含两个 Vue 文件是只有模板重复这一场景的标准样例TicketCard.vue使用script setup组合式 APIstyle scoped作用域样式TicketPreview.vue使用 Options APIexport default { ... }全局style。两个文件从article classticket到/article的template体逐行相同而脚本体与样式体各不相同。从仓库根目录按默认阈值运行jscpd fixtures/sfc-demo/template-range # Clone found (html) # - TicketCard.vue:html [2:3 - 15:13] (14 lines, 156 tokens) # TicketPreview.vue:html [2:3 - 15:13] # Found 1 clones.输出解读#行为控制台 reporter 的展示格式Found N clones.是控制台 reporter 的汇总行只发现 1 处克隆Found 1 clones.克隆类型为html——发生在模板块内两边都精确指向[2:3 - 15:13]第 2 行第 3 列template后第一行article起始处到第 15 行第 13 列/article结束处即模板体首行首列到末行末列模板边界被精确还原脚本与样式块完全未进入重复行数统计——尽管 TicketCard 的formatDate来自 import、TicketPreview 的formatDate是 methods 方法两者写法不同即便有相似也不会被误报。若想观察其余 SFC 语言的表现仓库还提供了 fixtures/svelte、fixtures/astro 与 fixtures/vue 等配套 fixture可用于对比三种框架下块划分的差异。四、底层原理wrapper 标签为什么永远不进 HTML 流Vue 与 Svelte/Astro 在哪些标记算 HTML 流上存在一个关键差异这也是 sfc-demo 文档的核心结论Vue 文件标记全部位于template内块外只剩下template、/template、script ...、/script、style ...、/style这些 wrapper 标签。它们在每个 Vue 文件中都完全相同且位于文件两端如果被扫进 HTML 流会让每一处模板克隆都被拉长到最后的/style并把脚本、样式体全部计为重复 HTML 行。因此 Vue 的 wrapper 标签一律排除在 HTML 流之外——无论有没有template块纯 script-only 组件同样只有 wrapper 标签裸露在块外。Svelte 与 Astro 文件没有 template 包装层顶层标记本身就是 HTML 流直接整体参与扫描。这一设计在源码中有两处直接对应分块扫描侧tokenizersfc.rs 中wrapper_tags_only file_format vue第 66 行之后markup_detection.retain(...)与条件if !markup_detection.is_empty() !wrapper_tags_only第 71 行决定 html 流是否保留块间的空白范围由blank_ranges_preserve_newlines见 embedded.rs 第 41 行以空格覆盖、保留换行保证后续 token 的行号/偏移与磁盘文件完全一致。测试侧同名文件内的单元测试精确锁定行为——vue_html_map_holds_only_the_template_body断言 html 流的全部 token 都落在template与/template之间wrapper 标签绝不进入vue_without_template_has_no_html_map断言无template的纯脚本组件不会生成 html 映射仅凭 wrapper 标签不足以构成 html 流svelte_keeps_top_level_markup断言 Svelte 的顶层标记第 4 行的h1进入 html 映射印证顶层标记即 HTML 流。块体与骨架的行号对齐为了保证报告中的[行:列]坐标能直接定位到原始文件tokenizer 采用骨架 块体偏移的方式块体内容用其inner_start处的LineIndex定位token 化后通过offset_detection_tokens把 token 位移回原始偏移sfc.rs 第 86 行最后再按range[0]排序恢复源码顺序第 96-98 行。测试vue_html_map_tokens_remain_in_source_order验证了 html token 始终按源码顺序排列。这种位置始终指向原文件的约定正是克隆报告可以精确到[2:3 - 15:13]的原因。五、扩展观察SFC 拆块能力的边界从源码结构看该拆块机制还覆盖了几种边界情况可以一并了解Svelte 的script中出现style字符串svelte_script_containing_style_text_no_panic测试验证了script字符串内出现style.../style文本时不会误拆块、不会 panic且真实style块仍正确归类为 css未知lang不崩溃unknown_lang_does_not_panic保证script langunknownlang123之类的非法属性值安全回落Astro 前端代码块Astro 文件以---围栏开头的 frontmatter 会被识别为typescript块astro_frontmatter_block这是 Astro 与 Vue/Svelte 在块结构上的又一差异。六、小结jscpd 对单文件组件的处理可以概括为三条原则按块拆分、按语言扫描、按块上报。Vue 的template包装层与 Svelte/Astro 的顶层标记在处理上有意区分目的是让克隆报告如实反映只有模板重复的事实而不是被每个文件都相同的 wrapper 标签污染。通过 fixtures/sfc-demo/template-range 中的两个演示文件你可以用一条jscpd fixtures/sfc-demo/template-range命令快速验证报告只会出现 1 处html克隆脚本与样式差异被完全忽略这正是块级克隆检测的直观效果。赞分享开发工具代码质量静态分析【免费下载链接】jscpdCopy/paste detector for source code. 220 languages, Rust engine, SARIF/HTML/badge reporters, GitHub Action, MCP server for AI agents.项目地址https://gitcode.com/gh_mirrors/js/jscpd点击查看免费下载相关推荐一文搞懂 ComfyUI-WanVideoWrapper14B视频生成模型在消费级显卡上的实战指南一文搞懂 ComfyUI WanVideoWrapper14B视频生成模型在消费级显卡上的实战指南 如果你手里只有一张 24GB 甚至 8GB 的显卡却想玩开发工具代码质量静态分析Propel2 ORM完全指南现代PHP高性能数据映射解决方案详解Propel2 ORM完全指南现代PHP高性能数据映射解决方案详解 Propel2是一款开源的高性能对象关系映射ORM工具专为现代PHP应用设计。作为P后端vue-i18n 单文件组件 i18n 自定义块实战指南从零配置到源码级原理vue i18n 单文件组件 i18n 自定义块实战指南从零配置到源码级原理 vue i18n 为 Vue.js 提供了「单文件组件Single File前端国际化上一篇如何高效使用智能屏幕翻译工具终极操作指南下一篇终极指南5步快速掌握Chromium/V8广谱注入与深度修改技术创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考