[{"data":1,"prerenderedAt":1367},["ShallowReactive",2],{"navigation_docs":3,"-reference-rust-crates":239,"-reference-rust-crates-surround":1362},[4,29,94,114,184],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"Start",false,"\u002Fstart","1.start",[10,14,19,24],{"title":11,"path":7,"stem":12,"icon":13},"What jscpd finds","1.start\u002Findex","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fstart\u002Finstallation","1.start\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Quickstart","\u002Fstart\u002Fquickstart","1.start\u002F3.quickstart","i-lucide-rocket",{"title":25,"path":26,"stem":27,"icon":28},"Configuration basics","\u002Fstart\u002Fconfiguration","1.start\u002F4.configuration","i-lucide-settings-2",{"title":30,"icon":6,"path":31,"stem":32,"children":33,"page":6},"Guides","\u002Fguides","2.guides",[34,39,44,49,54,59,64,69,74,79,84,89],{"title":35,"path":36,"stem":37,"icon":38},"Use jscpd in CI","\u002Fguides\u002Fci","2.guides\u002F01.ci","i-lucide-github",{"title":40,"path":41,"stem":42,"icon":43},"Pre-commit hooks","\u002Fguides\u002Fpre-commit","2.guides\u002F02.pre-commit","i-lucide-git-commit",{"title":45,"path":46,"stem":47,"icon":48},"Fail only on new duplication","\u002Fguides\u002Fbaseline","2.guides\u002F03.baseline","i-lucide-git-compare",{"title":50,"path":51,"stem":52,"icon":53},"Monorepos","\u002Fguides\u002Fmonorepos","2.guides\u002F04.monorepos","i-lucide-folder-tree",{"title":55,"path":56,"stem":57,"icon":58},"Cross-format detection","\u002Fguides\u002Fcross-format","2.guides\u002F05.cross-format","i-lucide-layers",{"title":60,"path":61,"stem":62,"icon":63},"Agents","\u002Fguides\u002Fagents","2.guides\u002F06.agents","i-lucide-cpu",{"title":65,"path":66,"stem":67,"icon":68},"Editors","\u002Fguides\u002Feditors","2.guides\u002F07.editors","i-lucide-code",{"title":70,"path":71,"stem":72,"icon":73},"Duplication over git history","\u002Fguides\u002Fhistory","2.guides\u002F08.history","i-lucide-chart-no-axes-combined",{"title":75,"path":76,"stem":77,"icon":78},"Dashboard and health score","\u002Fguides\u002Fdashboard","2.guides\u002F09.dashboard","i-lucide-layout-dashboard",{"title":80,"path":81,"stem":82,"icon":83},"Dead code","\u002Fguides\u002Fdead-code","2.guides\u002F10.dead-code","i-lucide-ghost",{"title":85,"path":86,"stem":87,"icon":88},"Comparing two codebases","\u002Fguides\u002Fcompare","2.guides\u002F11.compare","i-lucide-git-compare-arrows",{"title":90,"path":91,"stem":92,"icon":93},"Semantic clones","\u002Fguides\u002Fsemantic-clones","2.guides\u002F12.semantic-clones","i-lucide-brain-circuit",{"title":95,"icon":6,"path":96,"stem":97,"children":98,"page":6},"Concepts","\u002Fconcepts","3.concepts",[99,104,109],{"title":100,"path":101,"stem":102,"icon":103},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":105,"path":106,"stem":107,"icon":108},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":110,"path":111,"stem":112,"icon":113},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":115,"icon":6,"path":116,"stem":117,"children":118,"page":6},"Reference","\u002Freference","4.reference",[119,124,129,134,171,176,180],{"title":120,"path":121,"stem":122,"icon":123},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":125,"path":126,"stem":127,"icon":128},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":130,"path":131,"stem":132,"icon":133},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":135,"icon":6,"path":136,"stem":137,"children":138},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[139,141,146,151,156,161,166],{"title":135,"path":136,"stem":137,"icon":140},"i-lucide-file-chart-column",{"title":142,"path":143,"stem":144,"icon":145},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":147,"path":148,"stem":149,"icon":150},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":152,"path":153,"stem":154,"icon":155},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":157,"path":158,"stem":159,"icon":160},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":162,"path":163,"stem":164,"icon":165},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":167,"path":168,"stem":169,"icon":170},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":172,"path":173,"stem":174,"icon":175},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":177,"path":178,"stem":179,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":181,"path":182,"stem":183,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":185,"icon":6,"path":186,"stem":187,"children":188,"page":6},"Project","\u002Fproject","5.project",[189,194,199,204,229,234],{"title":190,"path":191,"stem":192,"icon":193},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":195,"path":196,"stem":197,"icon":198},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":200,"path":201,"stem":202,"icon":203},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":205,"icon":6,"path":206,"stem":207,"children":208},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[209,211,216,220,225],{"title":205,"path":206,"stem":207,"icon":210},"i-lucide-gauge",{"title":212,"path":213,"stem":214,"icon":215},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":217,"path":218,"stem":219,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":221,"path":222,"stem":223,"icon":224},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":226,"path":227,"stem":228,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":230,"path":231,"stem":232,"icon":233},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":235,"path":236,"stem":237,"icon":238},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",{"id":240,"title":181,"body":241,"description":1355,"extension":1356,"links":1357,"meta":1358,"navigation":1359,"path":182,"seo":1360,"stem":183,"__hash__":1361},"docs\u002F4.reference\u002F7.rust-crates.md",{"type":242,"value":243,"toc":1345},"minimark",[244,279,426,437,440,500,523,685,731,733,824,843,935,952,954,991,1004,1039,1047,1061,1063,1098,1116,1185,1194,1196,1229,1231,1249,1253,1315,1319,1341],[245,246,247,248,252,253,256,257,259,260,263,264,267,268,271,272,278],"p",{},"The engine of jscpd is a Cargo workspace of library crates on crates.io, and the command-line tool is the ",[249,250,251],"code",{},"jscpd"," crate (",[249,254,255],{},"cargo install jscpd"," gives you the ",[249,258,251],{}," and ",[249,261,262],{},"cpd"," binaries). The CLI crate's own library target is internal and changes without notice; a Rust program that embeds detection depends on the engine crates below. From any other language, run the binary with the ",[265,266,147],"a",{"href":148}," or talk to it over ",[265,269,270],{"href":61},"MCP","; the Node.js API of v4 lives on the ",[265,273,274,277],{"href":201},[249,275,276],{},"master-v4"," branch",".",[280,281,282,298],"table",{},[283,284,285],"thead",{},[286,287,288,292,295],"tr",{},[289,290,291],"th",{},"Crate",[289,293,294],{},"Version",[289,296,297],{},"What it holds",[299,300,301,320,336,353,370,390,410],"tbody",{},[286,302,303,314,317],{},[304,305,306],"td",{},[265,307,311],{"href":308,"rel":309},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fcpd-finder",[310],"nofollow",[249,312,313],{},"cpd-finder",[304,315,316],{},"0.1.20",[304,318,319],{},"The pipeline: walking files, tokenizing, detecting, statistics, git blame. The entry point",[286,321,322,331,333],{},[304,323,324],{},[265,325,328],{"href":326,"rel":327},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fcpd-core",[310],[249,329,330],{},"cpd-core",[304,332,316],{},[304,334,335],{},"The data model, the rolling-hash detector, gap merging, function similarity, and the summary, health and history types",[286,337,338,347,350],{},[304,339,340],{},[265,341,344],{"href":342,"rel":343},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fcpd-tokenizer",[310],[249,345,346],{},"cpd-tokenizer",[304,348,349],{},"0.1.19",[304,351,352],{},"Tokenizers for the 224 formats, with oxc for JavaScript and TypeScript and per-block tokenizing for Vue, Svelte, Astro, Markdown and Razor. No file or network access",[286,354,355,364,367],{},[304,356,357],{},[265,358,361],{"href":359,"rel":360},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fcpd-reporter",[310],[249,362,363],{},"cpd-reporter",[304,365,366],{},"0.1.21",[304,368,369],{},"The 15 reporters and the rule ids they share",[286,371,372,381,384],{},[304,373,374],{},[265,375,378],{"href":376,"rel":377},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fcpd-semantic",[310],[249,379,380],{},"cpd-semantic",[304,382,383],{},"0.1.1",[304,385,386,389],{},[249,387,388],{},"--semantic",": function extraction, embeddings, calibrated thresholds and a vector cache, as a clone pass for the finder",[286,391,392,401,404],{},[304,393,394],{},[265,395,398],{"href":396,"rel":397},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fbasta",[310],[249,399,400],{},"basta",[304,402,403],{},"0.3.0",[304,405,406,407],{},"The dead-code engine behind ",[249,408,409],{},"--dead-code",[286,411,412,420,423],{},[304,413,414],{},[265,415,418],{"href":416,"rel":417},"https:\u002F\u002Fcrates.io\u002Fcrates\u002Fjscpd",[310],[249,419,251],{},[304,421,422],{},"5.4.0",[304,424,425],{},"The CLI",[245,427,428,429,432,433,436],{},"The examples below compile against these versions with the ",[249,430,431],{},"Cargo.toml"," lines shown; ",[249,434,435],{},"cargo check"," against the published crates of jscpd 5.4.0 confirmed them.",[438,439,313],"h2",{"id":313},[245,441,442,445,446,449,450,453,454,453,457,453,460,453,463,453,466,453,469,453,472,453,475,453,478,481,482,485,486,489,490,492,493,495,496,499],{},[249,443,444],{},"cpd_finder::orchestrate"," runs the whole pipeline. ",[249,447,448],{},"RunConfig"," mirrors the CLI flags (",[249,451,452],{},"min_tokens",", ",[249,455,456],{},"min_lines",[249,458,459],{},"max_gap_lines",[249,461,462],{},"similarity",[249,464,465],{},"mode",[249,467,468],{},"formats",[249,470,471],{},"ignore",[249,473,474],{},"cross_formats",[249,476,477],{},"kinds",[249,479,480],{},"workers",", the Type-2 switches and more), ",[249,483,484],{},"run"," returns the clones, the statistics and the sources it read, and ",[249,487,488],{},"run_excluding"," leaves out folders. ",[249,491,484],{}," fails only when a clone pass fails, and only ",[249,494,388],{}," adds one, so a run without passes can be unwrapped. The ",[249,497,498],{},"source_id"," of a fragment comes back as an absolute path; the CLI strips the scan root before it prints.",[501,502,507],"pre",{"className":503,"code":504,"filename":431,"language":505,"meta":506,"style":506},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[dependencies]\ncpd-finder = \"0.1.20\"\n","toml","",[249,508,509,517],{"__ignoreMap":506},[510,511,514],"span",{"class":512,"line":513},"line",1,[510,515,516],{},"[dependencies]\n",[510,518,520],{"class":512,"line":519},2,[510,521,522],{},"cpd-finder = \"0.1.20\"\n",[501,524,529],{"className":525,"code":526,"filename":527,"language":528,"meta":506,"style":506},"language-rust shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","use std::path::PathBuf;\n\nuse cpd_finder::orchestrate::{RunConfig, run};\n\nfn main() {\n    let config = RunConfig {\n        paths: vec![PathBuf::from(\"src\")],\n        min_tokens: 50,\n        min_lines: 5,\n        ..Default::default()\n    };\n    let result = run(&config).expect(\"a run without passes cannot fail\");\n    println!(\"{} clones\", result.statistics.total.clones);\n    println!(\"{:.1}% duplication\", result.statistics.total.percentage);\n    for clone in &result.clones {\n        println!(\n            \"{}:{} ~ {}:{} ({:?}, {} tokens)\",\n            clone.fragment_a.source_id,\n            clone.fragment_a.start.line,\n            clone.fragment_b.source_id,\n            clone.fragment_b.start.line,\n            clone.kind,\n            clone.token_count,\n        );\n    }\n}\n","src\u002Fmain.rs","rust",[249,530,531,536,542,548,553,559,565,571,577,583,589,595,601,607,613,619,625,631,637,643,649,655,661,667,673,679],{"__ignoreMap":506},[510,532,533],{"class":512,"line":513},[510,534,535],{},"use std::path::PathBuf;\n",[510,537,538],{"class":512,"line":519},[510,539,541],{"emptyLinePlaceholder":540},true,"\n",[510,543,545],{"class":512,"line":544},3,[510,546,547],{},"use cpd_finder::orchestrate::{RunConfig, run};\n",[510,549,551],{"class":512,"line":550},4,[510,552,541],{"emptyLinePlaceholder":540},[510,554,556],{"class":512,"line":555},5,[510,557,558],{},"fn main() {\n",[510,560,562],{"class":512,"line":561},6,[510,563,564],{},"    let config = RunConfig {\n",[510,566,568],{"class":512,"line":567},7,[510,569,570],{},"        paths: vec![PathBuf::from(\"src\")],\n",[510,572,574],{"class":512,"line":573},8,[510,575,576],{},"        min_tokens: 50,\n",[510,578,580],{"class":512,"line":579},9,[510,581,582],{},"        min_lines: 5,\n",[510,584,586],{"class":512,"line":585},10,[510,587,588],{},"        ..Default::default()\n",[510,590,592],{"class":512,"line":591},11,[510,593,594],{},"    };\n",[510,596,598],{"class":512,"line":597},12,[510,599,600],{},"    let result = run(&config).expect(\"a run without passes cannot fail\");\n",[510,602,604],{"class":512,"line":603},13,[510,605,606],{},"    println!(\"{} clones\", result.statistics.total.clones);\n",[510,608,610],{"class":512,"line":609},14,[510,611,612],{},"    println!(\"{:.1}% duplication\", result.statistics.total.percentage);\n",[510,614,616],{"class":512,"line":615},15,[510,617,618],{},"    for clone in &result.clones {\n",[510,620,622],{"class":512,"line":621},16,[510,623,624],{},"        println!(\n",[510,626,628],{"class":512,"line":627},17,[510,629,630],{},"            \"{}:{} ~ {}:{} ({:?}, {} tokens)\",\n",[510,632,634],{"class":512,"line":633},18,[510,635,636],{},"            clone.fragment_a.source_id,\n",[510,638,640],{"class":512,"line":639},19,[510,641,642],{},"            clone.fragment_a.start.line,\n",[510,644,646],{"class":512,"line":645},20,[510,647,648],{},"            clone.fragment_b.source_id,\n",[510,650,652],{"class":512,"line":651},21,[510,653,654],{},"            clone.fragment_b.start.line,\n",[510,656,658],{"class":512,"line":657},22,[510,659,660],{},"            clone.kind,\n",[510,662,664],{"class":512,"line":663},23,[510,665,666],{},"            clone.token_count,\n",[510,668,670],{"class":512,"line":669},24,[510,671,672],{},"        );\n",[510,674,676],{"class":512,"line":675},25,[510,677,678],{},"    }\n",[510,680,682],{"class":512,"line":681},26,[510,683,684],{},"}\n",[245,686,687,688,453,691,694,695,698,699,702,703,706,707,710,711,453,714,702,717,720,721,702,724,727,728,730],{},"On a folder with two copies of one file this prints ",[249,689,690],{},"1 clones",[249,692,693],{},"50.0% duplication"," and one line that ends in ",[249,696,697],{},"(Exact, 124 tokens)",". The other modules are ",[249,700,701],{},"walker"," (",[249,704,705],{},"WalkConfig"," and file discovery that honors ",[249,708,709],{},".gitignore","), ",[249,712,713],{},"statistics::compute(sources, clones)",[249,715,716],{},"blame",[249,718,719],{},"BlameMap",") and ",[249,722,723],{},"pass",[249,725,726],{},"ClonePass",", the trait a pass such as ",[249,729,388],{}," implements).",[438,732,330],{"id":330},[245,734,735,736,739,740,743,744,747,748,453,751,453,754,453,756,710,759,453,762,259,765,768,769,710,772,259,775,778,779,782,783,453,786,453,789,720,792,795,796,799,800,803,804,807,808,799,811,453,814,453,817,259,820,823],{},"Types and algorithms with no I\u002FO. ",[249,737,738],{},"cpd_core::models"," holds ",[249,741,742],{},"CpdClone"," (two ",[249,745,746],{},"Fragment","s, ",[249,749,750],{},"token_count",[249,752,753],{},"kind",[249,755,462],{},[249,757,758],{},"is_new",[249,760,761],{},"CloneKind",[249,763,764],{},"Statistics",[249,766,767],{},"StatRow"," (the shape of the JSON report's ",[249,770,771],{},"statistics",[249,773,774],{},"SourceFile",[249,776,777],{},"Token",". ",[249,780,781],{},"cpd_core::detect"," holds the rolling-hash detector (",[249,784,785],{},"detect",[249,787,788],{},"detect_with_options",[249,790,791],{},"detect_prepared",[249,793,794],{},"merge_gapped_clones"," behind ",[249,797,798],{},"--max-gap-lines","; ",[249,801,802],{},"cpd_core::hash"," the Rabin-Karp window hash; ",[249,805,806],{},"cpd_core::similarity"," the function similarity of ",[249,809,810],{},"--similarity",[249,812,813],{},"summary",[249,815,816],{},"health",[249,818,819],{},"history",[249,821,822],{},"deadcode"," the types of the other modes.",[501,825,827],{"className":503,"code":826,"filename":431,"language":505,"meta":506,"style":506},"[dependencies]\ncpd-core = \"0.1.20\"\ncpd-tokenizer = \"0.1.19\"\n",[249,828,829,833,838],{"__ignoreMap":506},[510,830,831],{"class":512,"line":513},[510,832,516],{},[510,834,835],{"class":512,"line":519},[510,836,837],{},"cpd-core = \"0.1.20\"\n",[510,839,840],{"class":512,"line":544},[510,841,842],{},"cpd-tokenizer = \"0.1.19\"\n",[501,844,846],{"className":525,"code":845,"filename":527,"language":528,"meta":506,"style":506},"use cpd_core::detect::detect;\nuse cpd_core::models::SourceFile;\nuse cpd_tokenizer::tokenizer::{Mode, tokenize};\n\nfn main() {\n    let source = \"function area(w, h) {\\n  return w * h;\\n}\\n\";\n    let files: Vec\u003CSourceFile> = [\"a.js\", \"b.js\"]\n        .iter()\n        .map(|id| SourceFile {\n            id: id.to_string(),\n            format: \"javascript\".to_string(),\n            tokens: tokenize(\"javascript\", source, Mode::Mild),\n            bytes: source.len() as u64,\n        })\n        .collect();\n    let clones = detect(&files, 5);\n    println!(\"{} clones\", clones.len());\n}\n",[249,847,848,853,858,863,867,871,876,881,886,891,896,901,906,911,916,921,926,931],{"__ignoreMap":506},[510,849,850],{"class":512,"line":513},[510,851,852],{},"use cpd_core::detect::detect;\n",[510,854,855],{"class":512,"line":519},[510,856,857],{},"use cpd_core::models::SourceFile;\n",[510,859,860],{"class":512,"line":544},[510,861,862],{},"use cpd_tokenizer::tokenizer::{Mode, tokenize};\n",[510,864,865],{"class":512,"line":550},[510,866,541],{"emptyLinePlaceholder":540},[510,868,869],{"class":512,"line":555},[510,870,558],{},[510,872,873],{"class":512,"line":561},[510,874,875],{},"    let source = \"function area(w, h) {\\n  return w * h;\\n}\\n\";\n",[510,877,878],{"class":512,"line":567},[510,879,880],{},"    let files: Vec\u003CSourceFile> = [\"a.js\", \"b.js\"]\n",[510,882,883],{"class":512,"line":573},[510,884,885],{},"        .iter()\n",[510,887,888],{"class":512,"line":579},[510,889,890],{},"        .map(|id| SourceFile {\n",[510,892,893],{"class":512,"line":585},[510,894,895],{},"            id: id.to_string(),\n",[510,897,898],{"class":512,"line":591},[510,899,900],{},"            format: \"javascript\".to_string(),\n",[510,902,903],{"class":512,"line":597},[510,904,905],{},"            tokens: tokenize(\"javascript\", source, Mode::Mild),\n",[510,907,908],{"class":512,"line":603},[510,909,910],{},"            bytes: source.len() as u64,\n",[510,912,913],{"class":512,"line":609},[510,914,915],{},"        })\n",[510,917,918],{"class":512,"line":615},[510,919,920],{},"        .collect();\n",[510,922,923],{"class":512,"line":621},[510,924,925],{},"    let clones = detect(&files, 5);\n",[510,927,928],{"class":512,"line":627},[510,929,930],{},"    println!(\"{} clones\", clones.len());\n",[510,932,933],{"class":512,"line":633},[510,934,684],{},[245,936,937,938,778,940,942,943,945,946,259,949,278],{},"This prints ",[249,939,690],{},[249,941,785],{}," takes the minimum tokens; ",[249,944,788],{}," adds the minimum lines and the path filters of ",[249,947,948],{},"--skip-local",[249,950,951],{},"--skip-isolated",[438,953,346],{"id":346},[245,955,956,959,960,963,964,967,968,970,971,974,975,977,978,453,981,453,984,259,987,990],{},[249,957,958],{},"cpd_tokenizer::tokenizer::tokenize(format, source, mode)"," returns the tokens a reporter shows; ",[249,961,962],{},"tokenize_to_detection"," returns the hashed stream the detector consumes, with ",[249,965,966],{},"TokenizeOptions"," carrying the mode, case folding, ignore ranges and the Type-2 switches. ",[249,969,468],{}," knows the 224 formats and their extensions, ",[249,972,973],{},"functions"," extracts JavaScript and TypeScript functions for ",[249,976,810],{},", and ",[249,979,980],{},"sfc",[249,982,983],{},"markdown",[249,985,986],{},"razor",[249,988,989],{},"embedded"," split host files into blocks. The crate reads no files and opens no sockets, and CI enforces that.",[501,992,994],{"className":503,"code":993,"filename":431,"language":505,"meta":506,"style":506},"[dependencies]\ncpd-tokenizer = \"0.1.19\"\n",[249,995,996,1000],{"__ignoreMap":506},[510,997,998],{"class":512,"line":513},[510,999,516],{},[510,1001,1002],{"class":512,"line":519},[510,1003,842],{},[501,1005,1007],{"className":525,"code":1006,"filename":527,"language":528,"meta":506,"style":506},"use cpd_tokenizer::tokenizer::{Mode, tokenize};\n\nfn main() {\n    for token in tokenize(\"javascript\", \"const total = price * 2;\", Mode::Mild) {\n        println!(\"{:?} {:?} at {}:{}\", token.kind, token.value, token.start.line, token.start.column);\n    }\n}\n",[249,1008,1009,1013,1017,1021,1026,1031,1035],{"__ignoreMap":506},[510,1010,1011],{"class":512,"line":513},[510,1012,862],{},[510,1014,1015],{"class":512,"line":519},[510,1016,541],{"emptyLinePlaceholder":540},[510,1018,1019],{"class":512,"line":544},[510,1020,558],{},[510,1022,1023],{"class":512,"line":550},[510,1024,1025],{},"    for token in tokenize(\"javascript\", \"const total = price * 2;\", Mode::Mild) {\n",[510,1027,1028],{"class":512,"line":555},[510,1029,1030],{},"        println!(\"{:?} {:?} at {}:{}\", token.kind, token.value, token.start.line, token.start.column);\n",[510,1032,1033],{"class":512,"line":561},[510,1034,678],{},[510,1036,1037],{"class":512,"line":567},[510,1038,684],{},[501,1040,1045],{"className":1041,"code":1043,"language":1044,"meta":506},[1042],"language-text","Keyword \"const\" at 1:0\nIdentifier \"total\" at 1:6\nOperator \"=\" at 1:12\nIdentifier \"price\" at 1:14\nOperator \"*\" at 1:20\nLiteral \"2\" at 1:22\nPunctuation \";\" at 1:23\n","text",[249,1046,1043],{"__ignoreMap":506},[245,1048,1049,1052,1053,1056,1057,1060],{},[249,1050,1051],{},"Mode::Mild"," drops whitespace, ",[249,1054,1055],{},"Mode::Weak"," drops comments as well, and ",[249,1058,1059],{},"Mode::Strict"," keeps every token.",[438,1062,363],{"id":363},[245,1064,1065,1068,1069,1072,1073,453,1076,453,1078,1081,1082,1085,1086,1089,1090,1093,1094,1097],{},[249,1066,1067],{},"create_reporter(name, &options)"," returns any of the 15 reporters by its CLI name, aliases included. ",[249,1070,1071],{},"ReporterOptions"," carries the output directory, ",[249,1074,1075],{},"threshold",[249,1077,716],{},[249,1079,1080],{},"sarif_error_tokens"," and the tool version stamped into SARIF and HTML; ",[249,1083,1084],{},"ReportContext"," wraps the statistics, the duration and the optional summary and history. ",[249,1087,1088],{},"cpd_reporter::rules"," has the rule ids (",[249,1091,1092],{},"jscpd\u002Fduplicate-code"," and the others) that SARIF, CodeClimate and the language server share. A format of your own implements the ",[249,1095,1096],{},"Reporter"," trait.",[501,1099,1101],{"className":503,"code":1100,"filename":431,"language":505,"meta":506,"style":506},"[dependencies]\ncpd-finder = \"0.1.20\"\ncpd-reporter = \"0.1.21\"\n",[249,1102,1103,1107,1111],{"__ignoreMap":506},[510,1104,1105],{"class":512,"line":513},[510,1106,516],{},[510,1108,1109],{"class":512,"line":519},[510,1110,522],{},[510,1112,1113],{"class":512,"line":544},[510,1114,1115],{},"cpd-reporter = \"0.1.21\"\n",[501,1117,1119],{"className":525,"code":1118,"filename":527,"language":528,"meta":506,"style":506},"use std::path::PathBuf;\nuse std::time::Duration;\n\nuse cpd_finder::orchestrate::{RunConfig, run};\nuse cpd_reporter::{ReportContext, ReporterOptions, create_reporter};\n\nfn main() {\n    let config = RunConfig { paths: vec![PathBuf::from(\"src\")], ..Default::default() };\n    let result = run(&config).unwrap();\n    let options = ReporterOptions::new(PathBuf::from(\"report\"));\n    let reporter = create_reporter(\"json\", &options).expect(\"json is a built-in reporter\");\n    let ctx = ReportContext::new(&result.statistics, Duration::ZERO);\n    reporter.report(&result.clones, &ctx, &options.output_dir).unwrap();\n}\n",[249,1120,1121,1125,1130,1134,1138,1143,1147,1151,1156,1161,1166,1171,1176,1181],{"__ignoreMap":506},[510,1122,1123],{"class":512,"line":513},[510,1124,535],{},[510,1126,1127],{"class":512,"line":519},[510,1128,1129],{},"use std::time::Duration;\n",[510,1131,1132],{"class":512,"line":544},[510,1133,541],{"emptyLinePlaceholder":540},[510,1135,1136],{"class":512,"line":550},[510,1137,547],{},[510,1139,1140],{"class":512,"line":555},[510,1141,1142],{},"use cpd_reporter::{ReportContext, ReporterOptions, create_reporter};\n",[510,1144,1145],{"class":512,"line":561},[510,1146,541],{"emptyLinePlaceholder":540},[510,1148,1149],{"class":512,"line":567},[510,1150,558],{},[510,1152,1153],{"class":512,"line":573},[510,1154,1155],{},"    let config = RunConfig { paths: vec![PathBuf::from(\"src\")], ..Default::default() };\n",[510,1157,1158],{"class":512,"line":579},[510,1159,1160],{},"    let result = run(&config).unwrap();\n",[510,1162,1163],{"class":512,"line":585},[510,1164,1165],{},"    let options = ReporterOptions::new(PathBuf::from(\"report\"));\n",[510,1167,1168],{"class":512,"line":591},[510,1169,1170],{},"    let reporter = create_reporter(\"json\", &options).expect(\"json is a built-in reporter\");\n",[510,1172,1173],{"class":512,"line":597},[510,1174,1175],{},"    let ctx = ReportContext::new(&result.statistics, Duration::ZERO);\n",[510,1177,1178],{"class":512,"line":603},[510,1179,1180],{},"    reporter.report(&result.clones, &ctx, &options.output_dir).unwrap();\n",[510,1182,1183],{"class":512,"line":609},[510,1184,684],{},[245,1186,937,1187,1190,1191,278],{},[249,1188,1189],{},"JSON report saved to report\u002Fjscpd-report.json"," and writes the same file as ",[249,1192,1193],{},"jscpd src -r json -o report",[438,1195,380],{"id":380},[245,1197,1198,1199,1203,1204,1206,1207,702,1209,1212,1213,1220,1221,977,1223,1228],{},"The crate behind ",[265,1200,1201],{"href":91},[249,1202,388],{},": function extraction for Python, Rust and, through tree-sitter, C, C++, C#, Go, Java, Kotlin, PHP, Ruby, Scala and Swift (JavaScript and TypeScript come from ",[249,1205,346],{},"), embeddings from CodeRankEmbed or jina-embeddings-v2-base-code run in-process with candle or taken from an OpenAI-compatible API, the thresholds calibrated for nine models, a vector cache and the pairing rule. It plugs into the finder as a ",[249,1208,726],{},[249,1210,1211],{},"SemanticPass","), so the other crates build without its dependencies. Unlike them, it reads and writes the user cache directory and makes network calls for the model download and the API. The repository's ",[265,1214,1217],{"href":1215,"rel":1216},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Fblob\u002Fmaster\u002Fdocs\u002Fapi.md",[310],[249,1218,1219],{},"docs\u002Fapi.md"," has the snippet that adds the pass to a ",[249,1222,448],{},[265,1224,1227],{"href":1225,"rel":1226},"https:\u002F\u002Fdocs.rs\u002Fcpd-semantic",[310],"docs.rs"," has the API.",[438,1230,400],{"id":400},[245,1232,1233,1235,1236,259,1239,1242,1243,1245,1246,1248],{},[249,1234,409],{},", and the dead-code section of ",[249,1237,1238],{},"--dashboard",[249,1240,1241],{},"--health",", run the ",[249,1244,400],{}," crate: unused files, exports, symbols and imports across JavaScript, TypeScript and Python. It is a separate project with releases of its own; ",[265,1247,80],{"href":81}," covers the jscpd side.",[438,1250,1252],{"id":1251},"versions-and-features","Versions and features",[1254,1255,1256,1260,1263,1273,1282,1304],"ul",{},[1257,1258,1259],"li",{},"The engine crates version on their own, 0.1.x today, while the CLI is 5.4.0. One release publishes all of them from one workspace, so the versions in the table belong together.",[1257,1261,1262],{},"Minimum supported Rust version 1.96, edition 2024, MIT license.",[1257,1264,1265,1266,702,1269,1272],{},"Parallelism comes from rayon: tokenizing and detection run per format group on a local thread pool, and ",[249,1267,1268],{},"RunConfig::workers",[249,1270,1271],{},"--workers",") sets its size.",[1257,1274,1275,1276,453,1278,1281],{},"Cross-format detection (",[249,1277,474],{},[249,1279,1280],{},"--cross-formats",") and the embedded blocks of Vue, Svelte, Astro, Markdown and Razor files are part of the tokenizer and the finder, so a library user gets them without extra setup.",[1257,1283,1284,1285,453,1289,453,1293,453,1297,453,1301,278],{},"API documentation is on docs.rs: ",[265,1286,313],{"href":1287,"rel":1288},"https:\u002F\u002Fdocs.rs\u002Fcpd-finder",[310],[265,1290,330],{"href":1291,"rel":1292},"https:\u002F\u002Fdocs.rs\u002Fcpd-core",[310],[265,1294,346],{"href":1295,"rel":1296},"https:\u002F\u002Fdocs.rs\u002Fcpd-tokenizer",[310],[265,1298,363],{"href":1299,"rel":1300},"https:\u002F\u002Fdocs.rs\u002Fcpd-reporter",[310],[265,1302,380],{"href":1225,"rel":1303},[310],[1257,1305,1306,1307,1310,1311,1314],{},"Coming from the v4 ",[249,1308,1309],{},"@jscpd\u002F*"," npm packages? The ",[265,1312,1313],{"href":196},"migration guide"," maps each one to its crate.",[438,1316,1318],{"id":1317},"related","Related",[1254,1320,1321,1331,1336],{},[1257,1322,1323,1325,1326,259,1328,278],{},[265,1324,15],{"href":16}," for ",[249,1327,255],{},[249,1329,1330],{},"cargo binstall jscpd",[1257,1332,1333,1335],{},[265,1334,60],{"href":61}," for the MCP server built into the binary.",[1257,1337,1338,1340],{},[265,1339,147],{"href":148}," for the report a program in another language reads.",[1342,1343,1344],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":506,"searchDepth":519,"depth":519,"links":1346},[1347,1348,1349,1350,1351,1352,1353,1354],{"id":313,"depth":519,"text":313},{"id":330,"depth":519,"text":330},{"id":346,"depth":519,"text":346},{"id":363,"depth":519,"text":363},{"id":380,"depth":519,"text":380},{"id":400,"depth":519,"text":400},{"id":1251,"depth":519,"text":1252},{"id":1317,"depth":519,"text":1318},"The library crates behind the jscpd binary, what each one exposes, a compiling example for each, and their current versions.","md",null,{},{"icon":68},{"title":181,"description":1355},"BIXc3hb_zg0r4zRgGeDdNLWRMHeTctV_XeLh_xWiOT0",[1363,1365],{"title":177,"path":178,"stem":179,"description":1364,"icon":38,"children":-1},"The inputs and outputs of kucherenko\u002Fjscpd@v5, generated from the action.yml of jscpd 5.4.0.",{"title":190,"path":191,"stem":192,"description":1366,"icon":193,"children":-1},"Release history and what's new in jscpd.",1791302980842]