[{"data":1,"prerenderedAt":1372},["ShallowReactive",2],{"navigation_docs":3,"-concepts-clone-types":239,"-concepts-clone-types-surround":1367},[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":100,"body":241,"description":1358,"extension":1359,"links":1360,"meta":1361,"navigation":1362,"path":101,"seo":1363,"stem":102,"__hash__":1366},"docs\u002F3.concepts\u002F1.clone-types.md",{"type":242,"value":243,"toc":1350},"minimark",[244,248,350,357,362,365,464,471,501,509,513,516,614,617,704,739,778,786,799,811,815,818,866,875,905,958,984,1007,1057,1079,1083,1093,1107,1152,1159,1163,1205,1250,1270,1292,1296,1346],[245,246,247],"p",{},"\"Duplicated code\" covers more than copy and paste. Clone-detection research sorts duplicates into four types by how much the copies have drifted apart, and every detector, jscpd included, draws its line somewhere in that scale. Knowing the types tells you what a scan can and cannot show, and which flag to reach for.",[249,250,251,270],"table",{},[252,253,254],"thead",{},[255,256,257,261,264,267],"tr",{},[258,259,260],"th",{},"Type",[258,262,263],{},"Also called",[258,265,266],{},"The copies differ in",[258,268,269],{},"jscpd",[271,272,273,288,312,333],"tbody",{},[255,274,275,279,282,285],{},[276,277,278],"td",{},"Type-1",[276,280,281],{},"exact clone",[276,283,284],{},"whitespace, layout, comments",[276,286,287],{},"default",[255,289,290,293,296,299],{},[276,291,292],{},"Type-2",[276,294,295],{},"renamed, parameterized clone",[276,297,298],{},"identifier names, literal values, annotations",[276,300,301,305,306,305,309],{},[302,303,304],"code",{},"--ignore-identifiers",", ",[302,307,308],{},"--ignore-literals",[302,310,311],{},"--ignore-annotations",[255,313,314,317,320,323],{},[276,315,316],{},"Type-3",[276,318,319],{},"near-miss, gapped clone",[276,321,322],{},"added, removed or changed statements",[276,324,325,328,329,332],{},[302,326,327],{},"--max-gap-lines N"," merges the pieces around a gap; ",[302,330,331],{},"--similarity RATIO"," compares whole JS\u002FTS functions by syntax tree",[255,334,335,338,341,344],{},[276,336,337],{},"Type-4",[276,339,340],{},"semantic clone",[276,342,343],{},"everything but the behavior",[276,345,346,349],{},[302,347,348],{},"--semantic"," compares functions with a code embedding model (experimental, 5.3.3+)",[351,352],"demo-video",{"alt":353,"caption":354,"poster":355,"src":356},"One function drifting through the four clone kinds jscpd 5.2 reports: an exact copy, a renamed copy found with --ignore-identifiers, a copy with an inserted line found with --max-gap-lines 2, and a rewritten function found with --similarity 0.7","One function, four ways to copy it: exact by default, renamed with --ignore-identifiers, an inserted line with --max-gap-lines 2, scattered edits with --similarity 0.7.","\u002Fvideo\u002Fclone-types-poster.jpg","\u002Fvideo\u002Fclone-types.mp4",[358,359,361],"h2",{"id":360},"type-1-exact-clones","Type-1: exact clones",[245,363,364],{},"Two fragments are Type-1 clones when their tokens are identical. Whitespace, line breaks and, depending on the mode, comments do not count.",[366,367,372],"pre",{"className":368,"code":369,"language":370,"meta":371,"style":371},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F a.js\nfunction total(items) {\n  let sum = 0;\n  for (const item of items) { sum += item.price; }\n  return sum;\n}\n\n\u002F\u002F b.js\nfunction total(items) {\n  let sum = 0;   \u002F\u002F running total\n  for (const item of items) {\n    sum += item.price;\n  }\n  return sum;\n}\n","javascript","",[302,373,374,382,388,394,400,406,412,419,425,430,436,442,448,454,459],{"__ignoreMap":371},[375,376,379],"span",{"class":377,"line":378},"line",1,[375,380,381],{},"\u002F\u002F a.js\n",[375,383,385],{"class":377,"line":384},2,[375,386,387],{},"function total(items) {\n",[375,389,391],{"class":377,"line":390},3,[375,392,393],{},"  let sum = 0;\n",[375,395,397],{"class":377,"line":396},4,[375,398,399],{},"  for (const item of items) { sum += item.price; }\n",[375,401,403],{"class":377,"line":402},5,[375,404,405],{},"  return sum;\n",[375,407,409],{"class":377,"line":408},6,[375,410,411],{},"}\n",[375,413,415],{"class":377,"line":414},7,[375,416,418],{"emptyLinePlaceholder":417},true,"\n",[375,420,422],{"class":377,"line":421},8,[375,423,424],{},"\u002F\u002F b.js\n",[375,426,428],{"class":377,"line":427},9,[375,429,387],{},[375,431,433],{"class":377,"line":432},10,[375,434,435],{},"  let sum = 0;   \u002F\u002F running total\n",[375,437,439],{"class":377,"line":438},11,[375,440,441],{},"  for (const item of items) {\n",[375,443,445],{"class":377,"line":444},12,[375,446,447],{},"    sum += item.price;\n",[375,449,451],{"class":377,"line":450},13,[375,452,453],{},"  }\n",[375,455,457],{"class":377,"line":456},14,[375,458,405],{},[375,460,462],{"class":377,"line":461},15,[375,463,411],{},[245,465,466,467,470],{},"jscpd finds these by default. The ",[302,468,469],{},"--mode"," option decides what \"identical\" ignores:",[472,473,474,481,487],"ul",{},[475,476,477,480],"li",{},[302,478,479],{},"mild"," (default) drops whitespace, so layout changes are invisible.",[475,482,483,486],{},[302,484,485],{},"weak"," also drops comments, so a re-commented copy still matches.",[475,488,489,492,493,496,497,500],{},[302,490,491],{},"strict"," keeps everything except blocks marked with ",[302,494,495],{},"jscpd:ignore-start"," \u002F ",[302,498,499],{},"jscpd:ignore-end",".",[245,502,503,504,500],{},"Type-1 is the category to gate in CI: an exact clone is nearly always a copy that should be a shared function, and the result is stable enough for a ",[505,506,508],"a",{"href":507},"\u002Freference\u002Fconfig-file#gate-on-new-duplication-only","baseline",[358,510,512],{"id":511},"type-2-renamed-clones","Type-2: renamed clones",[245,514,515],{},"A Type-2 clone is a Type-1 clone whose identifiers, literals or annotations were changed while the structure stayed the same. This is what a copy looks like after someone adapted it to a second use.",[366,517,519],{"className":368,"code":518,"language":370,"meta":371,"style":371},"\u002F\u002F cart.js\nexport function cartTotal(items, taxRate) {\n  let subtotal = 0;\n  for (const item of items) {\n    subtotal += item.price * item.quantity;\n  }\n  const tax = subtotal * taxRate;\n  return { subtotal, tax, total: subtotal + tax };\n}\n\n\u002F\u002F basket.js\nexport function basketTotal(entries, vatRate) {\n  let net = 0;\n  for (const entry of entries) {\n    net += entry.price * entry.quantity;\n  }\n  const vat = net * vatRate;\n  return { net, vat, gross: net + vat };\n}\n",[302,520,521,526,531,536,540,545,549,554,559,563,567,572,577,582,587,592,597,603,609],{"__ignoreMap":371},[375,522,523],{"class":377,"line":378},[375,524,525],{},"\u002F\u002F cart.js\n",[375,527,528],{"class":377,"line":384},[375,529,530],{},"export function cartTotal(items, taxRate) {\n",[375,532,533],{"class":377,"line":390},[375,534,535],{},"  let subtotal = 0;\n",[375,537,538],{"class":377,"line":396},[375,539,441],{},[375,541,542],{"class":377,"line":402},[375,543,544],{},"    subtotal += item.price * item.quantity;\n",[375,546,547],{"class":377,"line":408},[375,548,453],{},[375,550,551],{"class":377,"line":414},[375,552,553],{},"  const tax = subtotal * taxRate;\n",[375,555,556],{"class":377,"line":421},[375,557,558],{},"  return { subtotal, tax, total: subtotal + tax };\n",[375,560,561],{"class":377,"line":427},[375,562,411],{},[375,564,565],{"class":377,"line":432},[375,566,418],{"emptyLinePlaceholder":417},[375,568,569],{"class":377,"line":438},[375,570,571],{},"\u002F\u002F basket.js\n",[375,573,574],{"class":377,"line":444},[375,575,576],{},"export function basketTotal(entries, vatRate) {\n",[375,578,579],{"class":377,"line":450},[375,580,581],{},"  let net = 0;\n",[375,583,584],{"class":377,"line":456},[375,585,586],{},"  for (const entry of entries) {\n",[375,588,589],{"class":377,"line":461},[375,590,591],{},"    net += entry.price * entry.quantity;\n",[375,593,595],{"class":377,"line":594},16,[375,596,453],{},[375,598,600],{"class":377,"line":599},17,[375,601,602],{},"  const vat = net * vatRate;\n",[375,604,606],{"class":377,"line":605},18,[375,607,608],{},"  return { net, vat, gross: net + vat };\n",[375,610,612],{"class":377,"line":611},19,[375,613,411],{},[245,615,616],{},"A default scan reports nothing here, because every other token differs. jscpd 5.2 adds three flags that normalize token classes before hashing:",[249,618,619,632],{},[252,620,621],{},[255,622,623,626,629],{},[258,624,625],{},"Flag",[258,627,628],{},"Config key",[258,630,631],{},"Effect",[271,633,634,658,680],{},[255,635,636,640,645],{},[276,637,638],{},[302,639,304],{},[276,641,642],{},[302,643,644],{},"ignoreIdentifiers",[276,646,647,648,651,652,655,656],{},"every identifier hashes as ",[302,649,650],{},"$id","; keywords are kept, so ",[302,653,654],{},"for"," still has to match ",[302,657,654],{},[255,659,660,664,669],{},[276,661,662],{},[302,663,308],{},[276,665,666],{},[302,667,668],{},"ignoreLiterals",[276,670,671,672,675,676,679],{},"strings hash as ",[302,673,674],{},"$str",", numbers as ",[302,677,678],{},"$num","; a string never matches a number",[255,681,682,686,691],{},[276,683,684],{},[302,685,311],{},[276,687,688],{},[302,689,690],{},"ignoreAnnotations",[276,692,693,305,696,699,700,703],{},[302,694,695],{},"@Name",[302,697,698],{},"@a.b.Name"," and ",[302,701,702],{},"@Name(...)"," are dropped in Java, Kotlin, Scala, Groovy, Python, Dart, Swift, JavaScript and TypeScript",[366,705,709],{"className":706,"code":707,"language":708,"meta":371,"style":371},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","jscpd --ignore-identifiers src\u002F\n# Clone found (javascript, renamed)\n#  - basket.js [1:1 - 9:2] (9 lines, 57 tokens)\n#    cart.js [1:1 - 9:2]\n","bash",[302,710,711,723,729,734],{"__ignoreMap":371},[375,712,713,716,720],{"class":377,"line":378},[375,714,269],{"class":715},"sBMFI",[375,717,719],{"class":718},"sfazB"," --ignore-identifiers",[375,721,722],{"class":718}," src\u002F\n",[375,724,725],{"class":377,"line":384},[375,726,728],{"class":727},"sHwdD","# Clone found (javascript, renamed)\n",[375,730,731],{"class":377,"line":390},[375,732,733],{"class":727},"#  - basket.js [1:1 - 9:2] (9 lines, 57 tokens)\n",[375,735,736],{"class":377,"line":396},[375,737,738],{"class":727},"#    cart.js [1:1 - 9:2]\n",[245,740,741,742,745,746,749,750,753,754,757,758,761,762,765,766,769,770,773,774,777],{},"Clones found this way are labelled so they never blend into the exact ones. Every clone carries a ",[302,743,744],{},"kind",": ",[302,747,748],{},"exact"," when the raw tokens match, ",[302,751,752],{},"renamed"," when they match only after normalization. The console prints ",[302,755,756],{},"Clone found (javascript, renamed)",", the ",[505,759,760],{"href":148},"JSON report"," has a ",[302,763,764],{},"\"kind\""," field, and the ",[505,767,768],{"href":158},"SARIF report"," files renamed clones under the rule ",[302,771,772],{},"jscpd\u002Frenamed-code"," instead of ",[302,775,776],{},"jscpd\u002Fduplicate-code",", so GitHub code scanning shows them as a separate rule.",[779,780,781,782,785],"note",{},"Normalized runs find more and longer clones than exact runs, so their fingerprints differ. Keep a separate ",[302,783,784],{},"--baseline"," file for a normalized configuration; the one from an exact scan does not match it.",[245,787,788,791,792,699,795,798],{},[302,789,790],{},"--ignore-case"," is a much smaller step in the same direction: it folds ",[302,793,794],{},"Total",[302,796,797],{},"total"," together and nothing else.",[245,800,801,802,810],{},"The repository ships a runnable demo of each flag in ",[505,803,807],{"href":804,"rel":805},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Ftype2-demo",[806],"nofollow",[302,808,809],{},"fixtures\u002Ftype2-demo",", with the expected output of every command.",[358,812,814],{"id":813},"type-3-near-miss-clones","Type-3: near-miss clones",[245,816,817],{},"Type-3 clones are copies with statements added, removed or changed in the middle: a validation line inserted here, a logging call dropped there.",[366,819,821],{"className":368,"code":820,"language":370,"meta":371,"style":371},"\u002F\u002F original                          \u002F\u002F copy with an inserted check\nfunction save(user) {                function save(user) {\n  const row = toRow(user);             const row = toRow(user);\n  row.updatedAt = Date.now();          if (!row.id) throw new Error('id');\n  db.put(row);                         row.updatedAt = Date.now();\n  audit('save', row.id);               db.put(row);\n}                                      audit('save', row.id);\n                                     }\n",[302,822,823,831,836,841,846,851,856,861],{"__ignoreMap":371},[375,824,825,828],{"class":377,"line":378},[375,826,827],{},"\u002F\u002F original",[375,829,830],{},"                          \u002F\u002F copy with an inserted check\n",[375,832,833],{"class":377,"line":384},[375,834,835],{},"function save(user) {                function save(user) {\n",[375,837,838],{"class":377,"line":390},[375,839,840],{},"  const row = toRow(user);             const row = toRow(user);\n",[375,842,843],{"class":377,"line":396},[375,844,845],{},"  row.updatedAt = Date.now();          if (!row.id) throw new Error('id');\n",[375,847,848],{"class":377,"line":402},[375,849,850],{},"  db.put(row);                         row.updatedAt = Date.now();\n",[375,852,853],{"class":377,"line":408},[375,854,855],{},"  audit('save', row.id);               db.put(row);\n",[375,857,858],{"class":377,"line":414},[375,859,860],{},"}                                      audit('save', row.id);\n",[375,862,863],{"class":377,"line":421},[375,864,865],{},"                                     }\n",[245,867,868,869,699,872,500],{},"A token-window detector sees this as two shorter exact clones with a gap between them, and that is what a default jscpd run reports, provided each side of the gap still clears ",[302,870,871],{},"--min-tokens",[302,873,874],{},"--min-lines",[245,876,877,878,880,881,884,885,888,889,892,893,896,897,900,901,904],{},"From jscpd 5.2, ",[302,879,327],{}," (config key ",[302,882,883],{},"maxGapLines",") merges clones of the same file pair whose fragments follow each other in both files with at most ",[302,886,887],{},"N"," unmatched lines between them into one clone of kind ",[302,890,891],{},"similar",". Its ",[302,894,895],{},"tokens"," value is the number of matched tokens and ",[302,898,899],{},"similarity"," is that number divided by the tokens of the longer merged span, so one inserted line in a 150-token block reads as roughly ",[302,902,903],{},"0.9",":",[366,906,908],{"className":706,"code":907,"language":708,"meta":371,"style":371},"jscpd src\u002F\n# Found 2 clones.\n\njscpd --max-gap-lines 1 src\u002F\n# Clone found (javascript, similar (gap) ~0.91)\n#  - save-account.js [1:1 - 12:2] (12 lines, 157 tokens)\n#    save-user.js [1:1 - 11:2]\n# Found 1 clones.\n",[302,909,910,916,921,925,938,943,948,953],{"__ignoreMap":371},[375,911,912,914],{"class":377,"line":378},[375,913,269],{"class":715},[375,915,722],{"class":718},[375,917,918],{"class":377,"line":384},[375,919,920],{"class":727},"# Found 2 clones.\n",[375,922,923],{"class":377,"line":390},[375,924,418],{"emptyLinePlaceholder":417},[375,926,927,929,932,936],{"class":377,"line":396},[375,928,269],{"class":715},[375,930,931],{"class":718}," --max-gap-lines",[375,933,935],{"class":934},"sbssI"," 1",[375,937,722],{"class":718},[375,939,940],{"class":377,"line":402},[375,941,942],{"class":727},"# Clone found (javascript, similar (gap) ~0.91)\n",[375,944,945],{"class":377,"line":408},[375,946,947],{"class":727},"#  - save-account.js [1:1 - 12:2] (12 lines, 157 tokens)\n",[375,949,950],{"class":377,"line":414},[375,951,952],{"class":727},"#    save-user.js [1:1 - 11:2]\n",[375,954,955],{"class":377,"line":421},[375,956,957],{"class":727},"# Found 1 clones.\n",[245,959,960,961,964,965,968,969,972,973,976,977,500],{},"The merge only joins clones the exact run already found, so it cannot invent a match; it removes fragmentation. It is off at the default of ",[302,962,963],{},"0",", and the JSON report carries ",[302,966,967],{},"\"kind\": \"similar\""," with the ",[302,970,971],{},"\"similarity\""," value, while SARIF files these under ",[302,974,975],{},"jscpd\u002Fsimilar-code",". A runnable pair lives in ",[505,978,981],{"href":979,"rel":980},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Ftype3-demo",[806],[302,982,983],{},"fixtures\u002Ftype3-demo",[245,985,986,987,989,990,992,993,996,997,1000,1001,1003,1004,904],{},"Edits spread through a whole function, with no single gap, still escape a token window. For JavaScript and TypeScript, ",[302,988,331],{}," compares whole functions by structure instead: each function, method or arrow function is summarized by the 4-grams of its syntax-tree node types, and two functions are reported as one ",[302,991,891],{}," clone when the weighted Jaccard index of those bags reaches ",[302,994,995],{},"RATIO",". Names and values are not part of the summary, so a renamed copy scores ",[302,998,999],{},"1.0",", one inserted line about ",[302,1002,903],{},", and two inserted statements plus renames about ",[302,1005,1006],{},"0.75",[366,1008,1010],{"className":706,"code":1009,"language":708,"meta":371,"style":371},"jscpd --similarity 0.85 src\u002F     # near-identical structure\njscpd --similarity 0.7 src\u002F      # a couple of added or removed statements\n# Clone found (javascript, similar (ast) ~0.75)\n#  - credit-note.js [1:8 - 19:2] (19 lines, 126 tokens)\n#    invoice.js [1:8 - 17:2]\n",[302,1011,1012,1028,1042,1047,1052],{"__ignoreMap":371},[375,1013,1014,1016,1019,1022,1025],{"class":377,"line":378},[375,1015,269],{"class":715},[375,1017,1018],{"class":718}," --similarity",[375,1020,1021],{"class":934}," 0.85",[375,1023,1024],{"class":718}," src\u002F",[375,1026,1027],{"class":727},"     # near-identical structure\n",[375,1029,1030,1032,1034,1037,1039],{"class":377,"line":384},[375,1031,269],{"class":715},[375,1033,1018],{"class":718},[375,1035,1036],{"class":934}," 0.7",[375,1038,1024],{"class":718},[375,1040,1041],{"class":727},"      # a couple of added or removed statements\n",[375,1043,1044],{"class":377,"line":390},[375,1045,1046],{"class":727},"# Clone found (javascript, similar (ast) ~0.75)\n",[375,1048,1049],{"class":377,"line":396},[375,1050,1051],{"class":727},"#  - credit-note.js [1:8 - 19:2] (19 lines, 126 tokens)\n",[375,1053,1054],{"class":377,"line":402},[375,1055,1056],{"class":727},"#    invoice.js [1:8 - 17:2]\n",[245,1058,1059,1060,699,1062,1064,1065,1068,1069,1071,1072,500],{},"Functions must clear ",[302,1061,871],{},[302,1063,874],{}," on their own, and a pair already reported as an exact or merged clone is not repeated. The MCP server finds these pairs when a tool call asks for them, and its ",[302,1066,1067],{},"check_duplication"," tool takes the ratio as ",[302,1070,899],{}," for the functions of a snippet. Both options are off by default. A runnable pair lives in ",[505,1073,1076],{"href":1074,"rel":1075},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Ftype3-demo\u002Fsimilar-functions",[806],[302,1077,1078],{},"fixtures\u002Ftype3-demo\u002Fsimilar-functions",[358,1080,1082],{"id":1081},"type-4-semantic-clones","Type-4: semantic clones",[245,1084,1085,1086,1088,1089,1092],{},"Type-4 clones compute the same thing with different code, such as a ",[302,1087,654],{}," loop and a ",[302,1090,1091],{},"reduce"," call that both sum prices, two sorting routines, or one rule written in Rust for a backend and again in TypeScript for its frontend. No token run and no syntax tree connects the copies, so the passes above cannot see them.",[245,1094,1095,1096,1098,1099,1102,1103,1106],{},"From jscpd 5.3.3, the experimental ",[302,1097,348],{}," mode looks for them with a code embedding model. It turns every function into a vector and reports two functions as a clone of kind ",[302,1100,1101],{},"semantic"," when each is the other's closest match and their vectors point the same way, within one language or across languages. The model runs inside jscpd after a one-time ",[302,1104,1105],{},"jscpd --semantic-download",", or behind an embeddings API.",[366,1108,1110],{"className":706,"code":1109,"language":708,"meta":371,"style":371},"jscpd --semantic-download                        # once: the model, 548 MB\njscpd --semantic src\u002F                            # pairs within one language and across languages\njscpd --semantic --semantic-scope cross .        # only pairs across languages\n",[302,1111,1112,1122,1134],{"__ignoreMap":371},[375,1113,1114,1116,1119],{"class":377,"line":378},[375,1115,269],{"class":715},[375,1117,1118],{"class":718}," --semantic-download",[375,1120,1121],{"class":727},"                        # once: the model, 548 MB\n",[375,1123,1124,1126,1129,1131],{"class":377,"line":384},[375,1125,269],{"class":715},[375,1127,1128],{"class":718}," --semantic",[375,1130,1024],{"class":718},[375,1132,1133],{"class":727},"                            # pairs within one language and across languages\n",[375,1135,1136,1138,1140,1143,1146,1149],{"class":377,"line":390},[375,1137,269],{"class":715},[375,1139,1128],{"class":718},[375,1141,1142],{"class":718}," --semantic-scope",[375,1144,1145],{"class":718}," cross",[375,1147,1148],{"class":718}," .",[375,1150,1151],{"class":727},"        # only pairs across languages\n",[245,1153,1154,1155,1158],{},"A similarity score says that two functions look alike to the model, and it cannot tell whether they behave alike, so review a pair before you merge the two functions. ",[505,1156,1157],{"href":91},"Semantic Clones"," covers the ways to use it and every option.",[358,1160,1162],{"id":1161},"reporting-one-kind","Reporting one kind",[245,1164,1165,1166,880,1169,1171,1172,305,1174,305,1176,1178,1179,305,1181,1184,1185,1188,1189,1184,1192,1195,1196,1184,1198,1200,1201,1204],{},"From jscpd 5.3.0, ",[302,1167,1168],{},"--kind",[302,1170,744],{},") keeps only the clones of the kinds it lists: ",[302,1173,748],{},[302,1175,752],{},[302,1177,891],{},", or one of the two mechanisms behind ",[302,1180,891],{},[302,1182,1183],{},"gap"," (",[302,1186,1187],{},"--max-gap-lines",") and ",[302,1190,1191],{},"ast",[302,1193,1194],{},"--similarity","). From 5.3.3 it also takes ",[302,1197,1101],{},[302,1199,348],{},"). Statistics and ",[302,1202,1203],{},"--threshold"," are computed after the filter, so the percentage describes what is reported.",[366,1206,1208],{"className":706,"code":1207,"language":708,"meta":371,"style":371},"jscpd --ignore-identifiers --kind renamed src\u002F                # only the renamed copies\njscpd --max-gap-lines 2 --similarity 0.7 --kind gap,ast src\u002F  # only near-miss clones\n",[302,1209,1210,1227],{"__ignoreMap":371},[375,1211,1212,1214,1216,1219,1222,1224],{"class":377,"line":378},[375,1213,269],{"class":715},[375,1215,719],{"class":718},[375,1217,1218],{"class":718}," --kind",[375,1220,1221],{"class":718}," renamed",[375,1223,1024],{"class":718},[375,1225,1226],{"class":727},"                # only the renamed copies\n",[375,1228,1229,1231,1233,1236,1238,1240,1242,1245,1247],{"class":377,"line":384},[375,1230,269],{"class":715},[375,1232,931],{"class":718},[375,1234,1235],{"class":934}," 2",[375,1237,1018],{"class":718},[375,1239,1036],{"class":934},[375,1241,1218],{"class":718},[375,1243,1244],{"class":718}," gap,ast",[375,1246,1024],{"class":718},[375,1248,1249],{"class":727},"  # only near-miss clones\n",[245,1251,1252,1253,1256,1257,1259,1260,1263,1264,500],{},"The filter never turns a detector on. ",[302,1254,1255],{},"--kind ast"," without ",[302,1258,1194],{}," prints ",[302,1261,1262],{},"Warning: --kind ast: no such clones are found without --similarity",", and an unknown kind is an error, so a typo cannot make a scan look clean. A runnable walk-through is in ",[505,1265,1268],{"href":1266,"rel":1267},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Ftype3-demo#keeping-one-kind---kind",[806],[302,1269,983],{},[245,1271,1272,1273,1277,1278,1281,1282,1285,1286,1289,1290,500],{},"The tools of the ",[505,1274,1276],{"href":1275},"\u002Fguides\u002Fagents#clone-types","MCP server"," take the same names in their ",[302,1279,1280],{},"kinds"," argument, and ",[302,1283,1284],{},"type1"," to ",[302,1287,1288],{},"type4"," too. There, asking for a kind turns its detector on, so a call that asks for renamed clones gets them even when the server runs without ",[302,1291,304],{},[358,1293,1295],{"id":1294},"which-type-to-look-for","Which type to look for",[472,1297,1298,1306,1316,1326,1340],{},[475,1299,1300,1301,699,1303,1305],{},"In CI, gate on Type-1. Exact clones are unambiguous and stable across runs, which is what a failing check needs. Use ",[302,1302,871],{},[302,1304,874],{}," to set the size, and a baseline to fail only on new duplication.",[475,1307,1308,1309,1312,1313,1315],{},"When refactoring, scan for Type-2. Run ",[302,1310,1311],{},"--ignore-identifiers --ignore-literals"," on the area you are about to change; renamed copies are where a shared helper pays off most. Read the ",[302,1314,752],{}," results as leads to check, since some parametric similarity is idiomatic.",[475,1317,1318,1319,1322,1323,1325],{},"Treat adjacent Type-1 findings as a pair. Two clones between the same files a few lines apart usually are one near-miss clone with an edit in between. Rerun with ",[302,1320,1321],{},"--max-gap-lines 2"," to see them as one and read the ",[302,1324,899],{}," value.",[475,1327,1328,1329,1331,1332,1335,1336,1339],{},"Hunt Type-3 in JavaScript and TypeScript with ",[302,1330,1194],{},". Start at ",[302,1333,1334],{},"0.85"," to catch near-identical functions, lower it towards ",[302,1337,1338],{},"0.7"," to see heavier edits. The score ranks the pairs; it does not judge them.",[475,1341,1342,1343,1345],{},"Look for Type-4 when two halves repeat each other. Run ",[302,1344,348],{}," on a backend and a frontend in different languages, on a library ported to another language, or on a code base where two teams solved one problem twice, and review each pair before you act on it.",[1347,1348,1349],"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);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":371,"searchDepth":384,"depth":384,"links":1351},[1352,1353,1354,1355,1356,1357],{"id":360,"depth":384,"text":361},{"id":511,"depth":384,"text":512},{"id":813,"depth":384,"text":814},{"id":1081,"depth":384,"text":1082},{"id":1161,"depth":384,"text":1162},{"id":1294,"depth":384,"text":1295},"Exact, renamed, near-miss and semantic clones, and which of them jscpd finds.","md",null,{},{"icon":103},{"title":1364,"description":1365},"Types of code clones (Type-1 to Type-4) and What jscpd Detects","The four clone types from clone-detection research, with examples, and how jscpd's modes and --ignore-identifiers \u002F --ignore-literals \u002F --ignore-annotations flags map onto them.","LjdvStDI6TCdxMUEYH5A5g1IAxeepfk21EFlJC-NShw",[1368,1370],{"title":90,"path":91,"stem":92,"description":1369,"icon":93,"children":-1},"Find functions that do the same job with different code, in one language or across languages, with jscpd --semantic and a code embedding model.",{"title":105,"path":106,"stem":107,"description":1371,"icon":108,"children":-1},"How jscpd turns source code into language tokens before it looks for duplicates, and what that means for the clones you get.",1791302983437]