[{"data":1,"prerenderedAt":2063},["ShallowReactive",2],{"navigation_docs":3,"-guides-boundaries":249,"-guides-boundaries-surround":2055,"page-views-\u002Fguides\u002Fboundaries":2060},[4,29,104,124,194],{"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,94,99],{"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,"path":96,"stem":97,"icon":98},"Similar functions","\u002Fguides\u002Fsimilarity","2.guides\u002F13.similarity","i-lucide-list-tree",{"title":100,"path":101,"stem":102,"icon":103},"Boundaries","\u002Fguides\u002Fboundaries","2.guides\u002F14.boundaries","i-lucide-fence",{"title":105,"icon":6,"path":106,"stem":107,"children":108,"page":6},"Concepts","\u002Fconcepts","3.concepts",[109,114,119],{"title":110,"path":111,"stem":112,"icon":113},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":115,"path":116,"stem":117,"icon":118},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":120,"path":121,"stem":122,"icon":123},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":125,"icon":6,"path":126,"stem":127,"children":128,"page":6},"Reference","\u002Freference","4.reference",[129,134,139,144,181,186,190],{"title":130,"path":131,"stem":132,"icon":133},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":135,"path":136,"stem":137,"icon":138},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":140,"path":141,"stem":142,"icon":143},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":145,"icon":6,"path":146,"stem":147,"children":148},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[149,151,156,161,166,171,176],{"title":145,"path":146,"stem":147,"icon":150},"i-lucide-file-chart-column",{"title":152,"path":153,"stem":154,"icon":155},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":157,"path":158,"stem":159,"icon":160},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":162,"path":163,"stem":164,"icon":165},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":167,"path":168,"stem":169,"icon":170},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":172,"path":173,"stem":174,"icon":175},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":177,"path":178,"stem":179,"icon":180},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":182,"path":183,"stem":184,"icon":185},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":187,"path":188,"stem":189,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":191,"path":192,"stem":193,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":195,"icon":6,"path":196,"stem":197,"children":198,"page":6},"Project","\u002Fproject","5.project",[199,204,209,214,239,244],{"title":200,"path":201,"stem":202,"icon":203},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":205,"path":206,"stem":207,"icon":208},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":210,"path":211,"stem":212,"icon":213},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":215,"icon":6,"path":216,"stem":217,"children":218},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[219,221,226,230,235],{"title":215,"path":216,"stem":217,"icon":220},"i-lucide-gauge",{"title":222,"path":223,"stem":224,"icon":225},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":227,"path":228,"stem":229,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":231,"path":232,"stem":233,"icon":234},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":236,"path":237,"stem":238,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":240,"path":241,"stem":242,"icon":243},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":245,"path":246,"stem":247,"icon":248},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",{"id":250,"title":100,"body":251,"description":2046,"extension":2047,"links":2048,"meta":2049,"navigation":2050,"path":101,"seo":2051,"stem":102,"__hash__":2054},"docs\u002F2.guides\u002F14.boundaries.md",{"type":252,"value":253,"toc":2029},"minimark",[254,258,261,266,292,504,530,566,569,577,580,586,605,629,633,649,654,684,815,841,856,874,877,881,892,941,947,1013,1017,1032,1036,1041,1148,1154,1158,1191,1254,1260,1264,1267,1316,1337,1344,1348,1371,1374,1380,1389,1400,1404,1430,1437,1455,1461,1487,1714,1721,1762,1768,1771,1775,1778,1861,1874,1878,1895,1901,1907,1913,1934,1938,1994,1998,2025],[255,256,257],"p",{},"Some code belongs in one place. In a product that handles money, the backend calculates every amount it charges, so a fee, a discount or a loan payment that the frontend calculates too is a second version of a backend function. When the formula changes on the backend, the copy keeps the old one, and the user sees one price and pays another. A boundary rule names the code to protect and the places its copies must stay out of. Every clone run checks the rules and exits with code 1 when it finds a copy, whether someone copied the code, edited a copy, or wrote the same logic again in another language.",[255,259,260],{},"Import linters such as dependency-cruiser, eslint-plugin-boundaries, ArchUnit or import-linter check which module imports which. A copied function imports nothing, so they don't see it.",[262,263,265],"h2",{"id":264},"quick-start","Quick start",[255,267,268,269,279,280,283,284,287,288,291],{},"The jscpd repository has a ready-made example in ",[270,271,275],"a",{"href":272,"rel":273},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Fboundaries-demo",[274],"nofollow",[276,277,278],"code",{},"fixtures\u002Fboundaries-demo",", a small shop that also gives loans. ",[276,281,282],{},"backend\u002Fpricing\u002F"," holds the discount and the sales tax in TypeScript, ",[276,285,286],{},"backend\u002Floans\u002FInterest.kt"," holds the monthly payment of a loan in Kotlin, and the demo's ",[276,289,290],{},".jscpd.json"," says that only the backend calculates money:",[293,294,299],"pre",{"className":295,"code":296,"filename":290,"language":297,"meta":298,"style":298},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"boundaries\": [\n    {\n      \"name\": \"money\",\n      \"code\": [\"backend\u002Fpricing\", \"backend\u002Floans\u002FInterest.kt#monthlyPayment\"],\n      \"notIn\": [\"web\u002F**\"],\n      \"allow\": [\"web\u002Fe2e\u002F**\"],\n      \"thresholds\": { \"minTokens\": 30, \"similarity\": 0.7 }\n    }\n  ]\n}\n","json","",[276,300,301,310,329,335,361,395,418,441,486,492,498],{"__ignoreMap":298},[302,303,306],"span",{"class":304,"line":305},"line",1,[302,307,309],{"class":308},"sMK4o","{\n",[302,311,313,316,320,323,326],{"class":304,"line":312},2,[302,314,315],{"class":308},"  \"",[302,317,319],{"class":318},"spNyl","boundaries",[302,321,322],{"class":308},"\"",[302,324,325],{"class":308},":",[302,327,328],{"class":308}," [\n",[302,330,332],{"class":304,"line":331},3,[302,333,334],{"class":308},"    {\n",[302,336,338,341,345,347,349,352,356,358],{"class":304,"line":337},4,[302,339,340],{"class":308},"      \"",[302,342,344],{"class":343},"sBMFI","name",[302,346,322],{"class":308},[302,348,325],{"class":308},[302,350,351],{"class":308}," \"",[302,353,355],{"class":354},"sfazB","money",[302,357,322],{"class":308},[302,359,360],{"class":308},",\n",[302,362,364,366,368,370,372,375,377,380,382,385,387,390,392],{"class":304,"line":363},5,[302,365,340],{"class":308},[302,367,276],{"class":343},[302,369,322],{"class":308},[302,371,325],{"class":308},[302,373,374],{"class":308}," [",[302,376,322],{"class":308},[302,378,379],{"class":354},"backend\u002Fpricing",[302,381,322],{"class":308},[302,383,384],{"class":308},",",[302,386,351],{"class":308},[302,388,389],{"class":354},"backend\u002Floans\u002FInterest.kt#monthlyPayment",[302,391,322],{"class":308},[302,393,394],{"class":308},"],\n",[302,396,398,400,403,405,407,409,411,414,416],{"class":304,"line":397},6,[302,399,340],{"class":308},[302,401,402],{"class":343},"notIn",[302,404,322],{"class":308},[302,406,325],{"class":308},[302,408,374],{"class":308},[302,410,322],{"class":308},[302,412,413],{"class":354},"web\u002F**",[302,415,322],{"class":308},[302,417,394],{"class":308},[302,419,421,423,426,428,430,432,434,437,439],{"class":304,"line":420},7,[302,422,340],{"class":308},[302,424,425],{"class":343},"allow",[302,427,322],{"class":308},[302,429,325],{"class":308},[302,431,374],{"class":308},[302,433,322],{"class":308},[302,435,436],{"class":354},"web\u002Fe2e\u002F**",[302,438,322],{"class":308},[302,440,394],{"class":308},[302,442,444,446,449,451,453,456,458,462,464,466,469,471,473,476,478,480,483],{"class":304,"line":443},8,[302,445,340],{"class":308},[302,447,448],{"class":343},"thresholds",[302,450,322],{"class":308},[302,452,325],{"class":308},[302,454,455],{"class":308}," {",[302,457,351],{"class":308},[302,459,461],{"class":460},"sbssI","minTokens",[302,463,322],{"class":308},[302,465,325],{"class":308},[302,467,468],{"class":460}," 30",[302,470,384],{"class":308},[302,472,351],{"class":308},[302,474,475],{"class":460},"similarity",[302,477,322],{"class":308},[302,479,325],{"class":308},[302,481,482],{"class":460}," 0.7",[302,484,485],{"class":308}," }\n",[302,487,489],{"class":304,"line":488},9,[302,490,491],{"class":308},"    }\n",[302,493,495],{"class":304,"line":494},10,[302,496,497],{"class":308},"  ]\n",[302,499,501],{"class":304,"line":500},11,[302,502,503],{"class":308},"}\n",[255,505,506,508,509,511,512,515,516,518,519,522,523,525,526,529],{},[276,507,276],{}," protects the whole ",[276,510,379],{}," folder and one method of ",[276,513,514],{},"Interest.kt",", ",[276,517,402],{}," keeps copies out of ",[276,520,521],{},"web\u002F",", and ",[276,524,425],{}," lets the end-to-end tests keep theirs. The search for logic written again in another language needs the embedding model of ",[276,527,528],{},"--semantic",", downloaded once:",[293,531,536],{"className":532,"code":533,"filename":534,"language":535,"meta":298,"style":298},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","jscpd --semantic-download     # once: 548 MB into the jscpd cache\ncd fixtures\u002Fboundaries-demo\njscpd .\n","Terminal","bash",[276,537,538,550,559],{"__ignoreMap":298},[302,539,540,543,546],{"class":304,"line":305},[302,541,542],{"class":343},"jscpd",[302,544,545],{"class":354}," --semantic-download",[302,547,549],{"class":548},"sHwdD","     # once: 548 MB into the jscpd cache\n",[302,551,552,556],{"class":304,"line":312},[302,553,555],{"class":554},"s2Zo4","cd",[302,557,558],{"class":354}," fixtures\u002Fboundaries-demo\n",[302,560,561,563],{"class":304,"line":331},[302,562,542],{"class":343},[302,564,565],{"class":354}," .\n",[255,567,568],{},"The clone report comes first, as usual. After it, the console lists what the rule found:",[293,570,575],{"className":571,"code":573,"language":574,"meta":298},[572],"language-text","Boundaries:\nmoney: 3 copies outside the boundary\n  web\u002Fsrc\u002Fcheckout\u002Fdiscount.ts:8-17 applyDiscount\n    copy of backend\u002Fpricing\u002Fdiscount.ts:6-15 applyDiscount (exact, 10 lines)\n  web\u002Fsrc\u002Fcheckout\u002Ftax.ts:6-15 estimateTax\n    looks like backend\u002Fpricing\u002Ftax.ts:5-13 salesTax (similar (ast) ~0.84)\n  web\u002Fsrc\u002Floan\u002Fpayment.ts:4-14 monthlyPayment\n    looks like backend\u002Floans\u002FInterest.kt:8-15 monthlyPayment (semantic ~0.82)\n","text",[276,576,573],{"__ignoreMap":298},[255,578,579],{},"The run ends with this line on stderr and exit code 1:",[293,581,584],{"className":582,"code":583,"language":574,"meta":298},[572],"ERROR: jscpd found 3 copies of protected code outside their boundaries (money: 3)\n",[276,585,583],{"__ignoreMap":298},[255,587,588,589,592,593,596,597,600,601,604],{},"Each copy comes first, with the function it lies in, and the protected code it copies sits under it. ",[276,590,591],{},"copy of"," marks copy-paste that token matching found, with its number of lines. ",[276,594,595],{},"looks like"," marks a function of the same structure, ",[276,598,599],{},"similar (ast)"," with its score, or a function that does the same job, ",[276,602,603],{},"semantic"," with its score. In the demo the discount was copied as it is, the tax was rewritten with other names and a debug line, and the monthly payment was written again in TypeScript.",[255,606,607,608,611,612,614,615,618,619,621,622,625,626,628],{},"The demo has three more copies that the rule leaves alone: the discount in ",[276,609,610],{},"web\u002Fe2e\u002F",", which ",[276,613,425],{}," takes; the discount in ",[276,616,617],{},"backend\u002Forders\u002F",", which is outside ",[276,620,402],{},"; and ",[276,623,624],{},"formatRate"," of ",[276,627,514],{}," written again in TypeScript, a method the rule doesn't protect.",[262,630,632],{"id":631},"write-a-rule","Write a rule",[255,634,635,637,638,642,643,645,646,648],{},[276,636,319],{}," in the ",[270,639,641],{"href":640},"\u002Freference\u002Fconfig-file#boundaries","config file"," is a list of rules. Each rule has a ",[276,644,344],{}," for the reports, unique in the file, and a ",[276,647,276],{}," list. The other keys are optional.",[650,651,653],"h3",{"id":652},"what-to-protect","What to protect",[255,655,656,658,659,662,663,666,667,670,671,674,675,515,678,662,681,325],{},[276,657,276],{}," takes paths and globs. A folder stands for everything in it, so ",[276,660,661],{},"backend\u002Fbilling"," and ",[276,664,665],{},"backend\u002Fbilling\u002F**"," protect the same files, and a glob such as ",[276,668,669],{},"backend\u002Floans\u002F**\u002F*Payment*.kt"," takes files by name. After a ",[276,672,673],{},"#",", a name takes one method of the matching files, and a pattern in parentheses takes every method whose name matches it, with ",[276,676,677],{},"*",[276,679,680],{},"?",[276,682,683],{},"{a,b}",[293,685,687],{"className":295,"code":686,"language":297,"meta":298,"style":298},"{\n  \"name\": \"money\",\n  \"code\": [\n    \"backend\u002Fbilling\",\n    \"backend\u002Floans\u002F**\u002F*Payment*.kt\",\n    \"backend\u002Floans\u002Fsrc\u002Fmain\u002Fkotlin\u002FInterest.kt#monthlyPayment\",\n    \"backend\u002Floans\u002Fsrc\u002Fmain\u002Fkotlin\u002FInterest.kt#(*Payments)\",\n    \"backend\u002F**\u002F*.kt#(calculate*)\"\n  ],\n  \"notIn\": [\"web\u002F**\", \"mobile\u002F**\"]\n}\n",[276,688,689,693,711,723,734,744,755,766,776,781,811],{"__ignoreMap":298},[302,690,691],{"class":304,"line":305},[302,692,309],{"class":308},[302,694,695,697,699,701,703,705,707,709],{"class":304,"line":312},[302,696,315],{"class":308},[302,698,344],{"class":318},[302,700,322],{"class":308},[302,702,325],{"class":308},[302,704,351],{"class":308},[302,706,355],{"class":354},[302,708,322],{"class":308},[302,710,360],{"class":308},[302,712,713,715,717,719,721],{"class":304,"line":331},[302,714,315],{"class":308},[302,716,276],{"class":318},[302,718,322],{"class":308},[302,720,325],{"class":308},[302,722,328],{"class":308},[302,724,725,728,730,732],{"class":304,"line":337},[302,726,727],{"class":308},"    \"",[302,729,661],{"class":354},[302,731,322],{"class":308},[302,733,360],{"class":308},[302,735,736,738,740,742],{"class":304,"line":363},[302,737,727],{"class":308},[302,739,669],{"class":354},[302,741,322],{"class":308},[302,743,360],{"class":308},[302,745,746,748,751,753],{"class":304,"line":397},[302,747,727],{"class":308},[302,749,750],{"class":354},"backend\u002Floans\u002Fsrc\u002Fmain\u002Fkotlin\u002FInterest.kt#monthlyPayment",[302,752,322],{"class":308},[302,754,360],{"class":308},[302,756,757,759,762,764],{"class":304,"line":420},[302,758,727],{"class":308},[302,760,761],{"class":354},"backend\u002Floans\u002Fsrc\u002Fmain\u002Fkotlin\u002FInterest.kt#(*Payments)",[302,763,322],{"class":308},[302,765,360],{"class":308},[302,767,768,770,773],{"class":304,"line":443},[302,769,727],{"class":308},[302,771,772],{"class":354},"backend\u002F**\u002F*.kt#(calculate*)",[302,774,775],{"class":308},"\"\n",[302,777,778],{"class":304,"line":488},[302,779,780],{"class":308},"  ],\n",[302,782,783,785,787,789,791,793,795,797,799,801,803,806,808],{"class":304,"line":494},[302,784,315],{"class":308},[302,786,402],{"class":318},[302,788,322],{"class":308},[302,790,325],{"class":308},[302,792,374],{"class":308},[302,794,322],{"class":308},[302,796,413],{"class":354},[302,798,322],{"class":308},[302,800,384],{"class":308},[302,802,351],{"class":308},[302,804,805],{"class":354},"mobile\u002F**",[302,807,322],{"class":308},[302,809,810],{"class":308},"]\n",[302,812,813],{"class":304,"line":500},[302,814,503],{"class":308},[255,816,817,820,821,824,825,522,828,830,831,834,835,837,838,840],{},[276,818,819],{},"Interest.kt#(*Payments)"," takes every method of the file whose name ends with ",[276,822,823],{},"Payments",", such as ",[276,826,827],{},"latePayments",[276,829,772],{}," takes the methods whose names start with ",[276,832,833],{},"calculate"," in every Kotlin file of the backend. A plain function counts as a method. jscpd finds methods in the languages of ",[276,836,528],{},": JavaScript, TypeScript and their Vue, Svelte and Astro components, Python, Rust, Go, Java, Kotlin, C#, C, C++, PHP, Ruby, Scala and Swift. In other formats, ",[276,839,276],{}," takes whole files.",[255,842,843,844,847,848,851,852,855],{},"A pattern needs its parentheses. ",[276,845,846],{},"Interest.kt#monthly*"," stops the run with an error that shows both forms, and ",[276,849,850],{},"Interest.kt#(monthly*)"," takes ",[276,853,854],{},"monthlyPayment",".",[255,857,858,859,862,863,865,866,869,870,873],{},"Paths start in the folder of the config file named with ",[276,860,861],{},"--config",", and in the working directory for a config file jscpd finds there. In a glob, ",[276,864,677],{}," stays in one folder and ",[276,867,868],{},"**"," crosses folders. An absolute path matches as it is. Square brackets match as written too, so ",[276,871,872],{},"web\u002Fapp\u002F[orderId]"," is the folder of a Next.js or SvelteKit route as well as a set of characters.",[255,875,876],{},"A copy breaks a method rule when it holds the whole method or a line inside it. A token clone that only starts on the method's last line, or ends on its first, copies the code around the method and doesn't count.",[650,878,880],{"id":879},"where-copies-must-not-show-up","Where copies must not show up",[255,882,883,885,886,888,889,891],{},[276,884,402],{}," takes globs of the places the rule keeps copies out of. Without ",[276,887,402],{},", every file outside ",[276,890,276],{}," counts, which suits a package whose code belongs nowhere else:",[293,893,895],{"className":295,"code":894,"language":297,"meta":298,"style":298},"{ \"name\": \"auth\", \"code\": [\"packages\u002Fauth\"] }\n",[276,896,897],{"__ignoreMap":298},[302,898,899,902,904,906,908,910,912,915,917,919,921,923,925,927,929,931,934,936,939],{"class":304,"line":305},[302,900,901],{"class":308},"{",[302,903,351],{"class":308},[302,905,344],{"class":318},[302,907,322],{"class":308},[302,909,325],{"class":308},[302,911,351],{"class":308},[302,913,914],{"class":354},"auth",[302,916,322],{"class":308},[302,918,384],{"class":308},[302,920,351],{"class":308},[302,922,276],{"class":318},[302,924,322],{"class":308},[302,926,325],{"class":308},[302,928,374],{"class":308},[302,930,322],{"class":308},[302,932,933],{"class":354},"packages\u002Fauth",[302,935,322],{"class":308},[302,937,938],{"class":308},"]",[302,940,485],{"class":308},[255,942,943,944,946],{},"The files of a rule's own ",[276,945,276],{}," never break it: two copies inside one module are plain duplication, which the clone report covers. So a rule can keep the code of one backend module out of the other backend modules:",[293,948,950],{"className":295,"code":949,"language":297,"meta":298,"style":298},"{ \"name\": \"billing\", \"code\": [\"backend\u002Fbilling\"], \"notIn\": [\"backend\u002F**\"] }\n",[276,951,952],{"__ignoreMap":298},[302,953,954,956,958,960,962,964,966,969,971,973,975,977,979,981,983,985,987,989,992,994,996,998,1000,1002,1004,1007,1009,1011],{"class":304,"line":305},[302,955,901],{"class":308},[302,957,351],{"class":308},[302,959,344],{"class":318},[302,961,322],{"class":308},[302,963,325],{"class":308},[302,965,351],{"class":308},[302,967,968],{"class":354},"billing",[302,970,322],{"class":308},[302,972,384],{"class":308},[302,974,351],{"class":308},[302,976,276],{"class":318},[302,978,322],{"class":308},[302,980,325],{"class":308},[302,982,374],{"class":308},[302,984,322],{"class":308},[302,986,661],{"class":354},[302,988,322],{"class":308},[302,990,991],{"class":308},"],",[302,993,351],{"class":308},[302,995,402],{"class":318},[302,997,322],{"class":308},[302,999,325],{"class":308},[302,1001,374],{"class":308},[302,1003,322],{"class":308},[302,1005,1006],{"class":354},"backend\u002F**",[302,1008,322],{"class":308},[302,1010,938],{"class":308},[302,1012,485],{"class":308},[650,1014,1016],{"id":1015},"where-a-copy-is-fine","Where a copy is fine",[255,1018,1019,1021,1022,1024,1025,1027,1028,1031],{},[276,1020,425],{}," takes globs of the places where copies may stay although ",[276,1023,402],{}," covers them, such as end-to-end tests that repeat a calculation to know the result they expect, or a generated API client. Test files count unless ",[276,1026,425],{}," takes them, although ",[276,1029,1030],{},"--similarity"," leaves them out of the clone report: for a rule, a copy in a test is a copy.",[650,1033,1035],{"id":1034},"how-close-a-copy-has-to-be","How close a copy has to be",[255,1037,1038,1040],{},[276,1039,448],{}," sets the bars of one rule. Each key replaces the project's setting for that rule only, and a key you leave out keeps the project's:",[1042,1043,1044,1060],"table",{},[1045,1046,1047],"thead",{},[1048,1049,1050,1054,1057],"tr",{},[1051,1052,1053],"th",{},"Key",[1051,1055,1056],{},"Replaces",[1051,1058,1059],{},"Values",[1061,1062,1063,1078,1092,1106,1120,1134],"tbody",{},[1048,1064,1065,1070,1075],{},[1066,1067,1068],"td",{},[276,1069,461],{},[1066,1071,1072],{},[276,1073,1074],{},"--min-tokens",[1066,1076,1077],{},"1 and up",[1048,1079,1080,1085,1090],{},[1066,1081,1082],{},[276,1083,1084],{},"minLines",[1066,1086,1087],{},[276,1088,1089],{},"--min-lines",[1066,1091,1077],{},[1048,1093,1094,1099,1104],{},[1066,1095,1096],{},[276,1097,1098],{},"minNodes",[1066,1100,1101],{},[276,1102,1103],{},"--min-nodes",[1066,1105,1077],{},[1048,1107,1108,1112,1117],{},[1066,1109,1110],{},[276,1111,475],{},[1066,1113,1114,1116],{},[276,1115,1030],{},", or 0.8 when the run sets none",[1066,1118,1119],{},"above 0, up to 1",[1048,1121,1122,1127,1132],{},[1066,1123,1124],{},[276,1125,1126],{},"semanticThreshold",[1066,1128,1129],{},[276,1130,1131],{},"--semantic-threshold",[1066,1133,1119],{},[1048,1135,1136,1141,1146],{},[1066,1137,1138],{},[276,1139,1140],{},"semanticSameThreshold",[1066,1142,1143],{},[276,1144,1145],{},"--semantic-same-threshold",[1066,1147,1119],{},[255,1149,1150,1151,1153],{},"For a rule like ",[276,1152,355],{}," a missed copy costs more than a false alarm, so its bars can sit below the ones the clone report uses: the demo looks for copies from 30 tokens and for functions of the same structure from a score of 0.7. A value out of range stops the run with an error.",[650,1155,1157],{"id":1156},"kinds-to-leave-out","Kinds to leave out",[255,1159,1160,1163,1164,1167,1168,515,1171,515,1174,515,1177,515,1180,1183,1184,662,1186,522,1188,1190],{},[276,1161,1162],{},"disable"," turns off kinds of copy by the names of ",[276,1165,1166],{},"--kind",": ",[276,1169,1170],{},"exact",[276,1172,1173],{},"renamed",[276,1175,1176],{},"gap",[276,1178,1179],{},"ast",[276,1181,1182],{},"similar"," for both ",[276,1185,1176],{},[276,1187,1179],{},[276,1189,603],{},". A rule whose two sides use one language can do without the model, and then needs no download and prints no warning about it:",[293,1192,1194],{"className":295,"code":1193,"language":297,"meta":298,"style":298},"{ \"name\": \"auth\", \"code\": [\"packages\u002Fauth\"], \"disable\": [\"semantic\"] }\n",[276,1195,1196],{"__ignoreMap":298},[302,1197,1198,1200,1202,1204,1206,1208,1210,1212,1214,1216,1218,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240,1242,1244,1246,1248,1250,1252],{"class":304,"line":305},[302,1199,901],{"class":308},[302,1201,351],{"class":308},[302,1203,344],{"class":318},[302,1205,322],{"class":308},[302,1207,325],{"class":308},[302,1209,351],{"class":308},[302,1211,914],{"class":354},[302,1213,322],{"class":308},[302,1215,384],{"class":308},[302,1217,351],{"class":308},[302,1219,276],{"class":318},[302,1221,322],{"class":308},[302,1223,325],{"class":308},[302,1225,374],{"class":308},[302,1227,322],{"class":308},[302,1229,933],{"class":354},[302,1231,322],{"class":308},[302,1233,991],{"class":308},[302,1235,351],{"class":308},[302,1237,1162],{"class":318},[302,1239,322],{"class":308},[302,1241,325],{"class":308},[302,1243,374],{"class":308},[302,1245,322],{"class":308},[302,1247,603],{"class":354},[302,1249,322],{"class":308},[302,1251,938],{"class":308},[302,1253,485],{"class":308},[255,1255,1256,1257,1259],{},"When a rule turns off the surer search for a copy, the next one reports it: with ",[276,1258,1170],{}," off, a copied function shows up as one of the same structure. A rule that turns off every kind stops the run with an error.",[262,1261,1263],{"id":1262},"how-jscpd-looks-for-copies","How jscpd looks for copies",[255,1265,1266],{},"After the scan of the clone report, jscpd runs three searches for every rule, with the rule's bars:",[1268,1269,1270,1294,1302],"ul",{},[1271,1272,1273,1274,1276,1277,1279,1280,515,1283,1286,1287,1290,1291,855],"li",{},"Token matching finds copy-paste within one language: ",[276,1275,1170],{}," copies always, ",[276,1278,1173],{}," ones with ",[276,1281,1282],{},"--ignore-identifiers",[276,1284,1285],{},"--ignore-literals"," or ",[276,1288,1289],{},"--ignore-annotations",", and copies merged across a gap with ",[276,1292,1293],{},"--max-gap-lines",[1271,1295,1296,1297,1301],{},"The structure search compares functions the way ",[270,1298,1299],{"href":96},[276,1300,1030],{}," does and finds a copy someone edited. It compares each protected function with the functions outside the boundary and no other pair.",[1271,1303,1304,1305,1310,1311,1315],{},"The semantic search pairs functions the way ",[270,1306,1307],{"href":86},[276,1308,1309],{},"--compare"," pairs two folders, with the model of ",[270,1312,1313],{"href":91},[276,1314,528],{},", and finds the logic written again, often in another language.",[255,1317,1318,1319,662,1321,1323,1324,515,1326,662,1329,1332,1333,1336],{},"The searches run whatever the clone report asks for. ",[276,1320,1030],{},[276,1322,528],{}," don't have to be on, and ",[276,1325,1166],{},[276,1327,1328],{},"--skip-local",[276,1330,1331],{},"--skip-isolated"," narrow the clone report but not the rules. The clone list, the statistics and ",[276,1334,1335],{},"--threshold"," stay as they are. A copy that two searches find is reported once, by the surer one: exact, then renamed, then merged across a gap, then the same structure, then the same job.",[255,1338,1339,1340,1343],{},"The rules see the files of the scan only. ",[276,1341,1342],{},"jscpd web\u002F"," reads no protected code and warns about it, so scan a path that holds both sides, usually the repository root.",[262,1345,1347],{"id":1346},"the-model","The model",[255,1349,1350,1351,1353,1354,1356,1357,1360,1361,1364,1365,1367,1368,1370],{},"The semantic search uses the model of ",[276,1352,528],{},", with the settings of the ",[276,1355,603],{}," section of the config file and the ",[276,1358,1359],{},"--semantic-*"," flags, ",[276,1362,1363],{},"--semantic-scope"," aside. jscpd embeds the functions of every rule in one call and caches the vectors where ",[276,1366,528],{}," does, so a later run embeds only the functions that changed. ",[270,1369,90],{"href":91}," covers the models, the cache folder and an embeddings API in place of the local model.",[255,1372,1373],{},"Without the model, jscpd prints a warning and checks the rules without the semantic search:",[293,1375,1378],{"className":1376,"code":1377,"language":574,"meta":298},[572],"Warning: boundaries: the model nomic-ai\u002FCodeRankEmbed is not downloaded, so the rules look for copies without it and miss logic written again in another language. Run `jscpd --semantic-download` once (548 MB), or use an embeddings API with --semantic-url\n",[276,1379,1377],{"__ignoreMap":298},[255,1381,1382,1383,1386,1387,855],{},"Copy-paste and edited copies still fail the run. The demo then reports 2 copies of 3, and the monthly payment written again in TypeScript goes unseen. jscpd does the same when there is no cache folder to keep the model in, and ",[276,1384,1385],{},"JSCPD_CACHE_DIR"," names one. Any other problem with the model, such as an embeddings API that doesn't answer, stops the run, as it does for ",[276,1388,528],{},[255,1390,1391,1392,1394,1395,1397,1398,855],{},"The semantic search is the least sure of the three. It can report related code, such as a client function and the endpoint it calls, so read a ",[276,1393,603],{}," violation before you act on it. When the pair is not a copy, raise the rule's ",[276,1396,1126],{}," or add the file to ",[276,1399,425],{},[262,1401,1403],{"id":1402},"read-the-report","Read the report",[255,1405,1406,662,1409,1412,1413,1417,1418,1421,1422,1425,1426,1429],{},[276,1407,1408],{},"console",[276,1410,1411],{},"console-full"," print the rules after the clone table, as in the ",[270,1414,1416],{"href":1415},"#quick-start","quick start",", and a rule without copies says ",[276,1419,1420],{},"auth: no copies outside the boundary",". With a copy, jscpd writes the ",[276,1423,1424],{},"ERROR:"," line to stderr and exits with code 1 whichever reporters run, ",[276,1427,1428],{},"--silent"," included.",[255,1431,1432,1433,1436],{},"The ",[276,1434,1435],{},"ai"," reporter prints one line per copy, the copy first, and a count:",[293,1438,1440],{"className":532,"code":1439,"filename":534,"language":535,"meta":298,"style":298},"jscpd . -r ai\n",[276,1441,1442],{"__ignoreMap":298},[302,1443,1444,1446,1449,1452],{"class":304,"line":305},[302,1445,542],{"class":343},[302,1447,1448],{"class":354}," .",[302,1450,1451],{"class":354}," -r",[302,1453,1454],{"class":354}," ai\n",[293,1456,1459],{"className":1457,"code":1458,"language":574,"meta":298},[572],"Boundaries:\nmoney: web\u002Fsrc\u002Fcheckout\u002Fdiscount.ts:8-17 ~ backend\u002Fpricing\u002Fdiscount.ts:6-15\nmoney: web\u002Fsrc\u002Fcheckout\u002Ftax.ts:6-15 ~ backend\u002Fpricing\u002Ftax.ts:5-13 [~0.84 ast]\nmoney: web\u002Fsrc\u002Floan\u002Fpayment.ts:4-14 ~ backend\u002Floans\u002FInterest.kt:8-15 [~0.82 semantic]\n3 copies outside boundaries\n",[276,1460,1458],{"__ignoreMap":298},[255,1462,1432,1463,1465,1466,1468,1469,1472,1473,1475,1476,1479,1480,662,1483,1486],{},[276,1464,297],{}," reporter adds a ",[276,1467,319],{}," key to ",[276,1470,1471],{},"jscpd-report.json",", with one entry per rule that holds its ",[276,1474,344],{}," and its ",[276,1477,1478],{},"violations",". The semantic copy of the demo, with ",[276,1481,1482],{},"startLoc",[276,1484,1485],{},"endLoc"," left out of both sides:",[293,1488,1491],{"className":295,"code":1489,"filename":1490,"language":297,"meta":298,"style":298},"{\n  \"kind\": \"semantic\",\n  \"similarity\": 0.82,\n  \"lines\": 11,\n  \"tokens\": 80,\n  \"copy\": { \"name\": \"web\u002Fsrc\u002Floan\u002Fpayment.ts\", \"start\": 4, \"end\": 14, \"function\": \"monthlyPayment\" },\n  \"protected\": { \"name\": \"backend\u002Floans\u002FInterest.kt\", \"start\": 8, \"end\": 15, \"function\": \"monthlyPayment\" }\n}\n","report\u002Fjscpd-report.json",[276,1492,1493,1497,1516,1531,1547,1563,1639,1710],{"__ignoreMap":298},[302,1494,1495],{"class":304,"line":305},[302,1496,309],{"class":308},[302,1498,1499,1501,1504,1506,1508,1510,1512,1514],{"class":304,"line":312},[302,1500,315],{"class":308},[302,1502,1503],{"class":318},"kind",[302,1505,322],{"class":308},[302,1507,325],{"class":308},[302,1509,351],{"class":308},[302,1511,603],{"class":354},[302,1513,322],{"class":308},[302,1515,360],{"class":308},[302,1517,1518,1520,1522,1524,1526,1529],{"class":304,"line":331},[302,1519,315],{"class":308},[302,1521,475],{"class":318},[302,1523,322],{"class":308},[302,1525,325],{"class":308},[302,1527,1528],{"class":460}," 0.82",[302,1530,360],{"class":308},[302,1532,1533,1535,1538,1540,1542,1545],{"class":304,"line":337},[302,1534,315],{"class":308},[302,1536,1537],{"class":318},"lines",[302,1539,322],{"class":308},[302,1541,325],{"class":308},[302,1543,1544],{"class":460}," 11",[302,1546,360],{"class":308},[302,1548,1549,1551,1554,1556,1558,1561],{"class":304,"line":363},[302,1550,315],{"class":308},[302,1552,1553],{"class":318},"tokens",[302,1555,322],{"class":308},[302,1557,325],{"class":308},[302,1559,1560],{"class":460}," 80",[302,1562,360],{"class":308},[302,1564,1565,1567,1570,1572,1574,1576,1578,1580,1582,1584,1586,1589,1591,1593,1595,1598,1600,1602,1605,1607,1609,1612,1614,1616,1619,1621,1623,1626,1628,1630,1632,1634,1636],{"class":304,"line":397},[302,1566,315],{"class":308},[302,1568,1569],{"class":318},"copy",[302,1571,322],{"class":308},[302,1573,325],{"class":308},[302,1575,455],{"class":308},[302,1577,351],{"class":308},[302,1579,344],{"class":343},[302,1581,322],{"class":308},[302,1583,325],{"class":308},[302,1585,351],{"class":308},[302,1587,1588],{"class":354},"web\u002Fsrc\u002Floan\u002Fpayment.ts",[302,1590,322],{"class":308},[302,1592,384],{"class":308},[302,1594,351],{"class":308},[302,1596,1597],{"class":343},"start",[302,1599,322],{"class":308},[302,1601,325],{"class":308},[302,1603,1604],{"class":460}," 4",[302,1606,384],{"class":308},[302,1608,351],{"class":308},[302,1610,1611],{"class":343},"end",[302,1613,322],{"class":308},[302,1615,325],{"class":308},[302,1617,1618],{"class":460}," 14",[302,1620,384],{"class":308},[302,1622,351],{"class":308},[302,1624,1625],{"class":343},"function",[302,1627,322],{"class":308},[302,1629,325],{"class":308},[302,1631,351],{"class":308},[302,1633,854],{"class":354},[302,1635,322],{"class":308},[302,1637,1638],{"class":308}," },\n",[302,1640,1641,1643,1646,1648,1650,1652,1654,1656,1658,1660,1662,1664,1666,1668,1670,1672,1674,1676,1679,1681,1683,1685,1687,1689,1692,1694,1696,1698,1700,1702,1704,1706,1708],{"class":304,"line":420},[302,1642,315],{"class":308},[302,1644,1645],{"class":318},"protected",[302,1647,322],{"class":308},[302,1649,325],{"class":308},[302,1651,455],{"class":308},[302,1653,351],{"class":308},[302,1655,344],{"class":343},[302,1657,322],{"class":308},[302,1659,325],{"class":308},[302,1661,351],{"class":308},[302,1663,286],{"class":354},[302,1665,322],{"class":308},[302,1667,384],{"class":308},[302,1669,351],{"class":308},[302,1671,1597],{"class":343},[302,1673,322],{"class":308},[302,1675,325],{"class":308},[302,1677,1678],{"class":460}," 8",[302,1680,384],{"class":308},[302,1682,351],{"class":308},[302,1684,1611],{"class":343},[302,1686,322],{"class":308},[302,1688,325],{"class":308},[302,1690,1691],{"class":460}," 15",[302,1693,384],{"class":308},[302,1695,351],{"class":308},[302,1697,1625],{"class":343},[302,1699,322],{"class":308},[302,1701,325],{"class":308},[302,1703,351],{"class":308},[302,1705,854],{"class":354},[302,1707,322],{"class":308},[302,1709,485],{"class":308},[302,1711,1712],{"class":304,"line":443},[302,1713,503],{"class":308},[255,1715,1432,1716,1720],{},[270,1717,1719],{"href":1718},"\u002Freference\u002Freporters\u002Fjson#boundaries","JSON reporter page"," lists every field. One line per copy from a script:",[293,1722,1724],{"className":532,"code":1723,"filename":534,"language":535,"meta":298,"style":298},"jscpd . -r json --output report\njq -r '.boundaries[] | .name as $rule | .violations[] | \"\\($rule): \\(.copy.name):\\(.copy.start) copies \\(.protected.name):\\(.protected.start) (\\(.kind))\"' report\u002Fjscpd-report.json\n",[276,1725,1726,1743],{"__ignoreMap":298},[302,1727,1728,1730,1732,1734,1737,1740],{"class":304,"line":305},[302,1729,542],{"class":343},[302,1731,1448],{"class":354},[302,1733,1451],{"class":354},[302,1735,1736],{"class":354}," json",[302,1738,1739],{"class":354}," --output",[302,1741,1742],{"class":354}," report\n",[302,1744,1745,1748,1750,1753,1756,1759],{"class":304,"line":312},[302,1746,1747],{"class":343},"jq",[302,1749,1451],{"class":354},[302,1751,1752],{"class":308}," '",[302,1754,1755],{"class":354},".boundaries[] | .name as $rule | .violations[] | \"\\($rule): \\(.copy.name):\\(.copy.start) copies \\(.protected.name):\\(.protected.start) (\\(.kind))\"",[302,1757,1758],{"class":308},"'",[302,1760,1761],{"class":354}," report\u002Fjscpd-report.json\n",[293,1763,1766],{"className":1764,"code":1765,"language":574,"meta":298},[572],"money: web\u002Fsrc\u002Fcheckout\u002Fdiscount.ts:8 copies backend\u002Fpricing\u002Fdiscount.ts:6 (exact)\nmoney: web\u002Fsrc\u002Fcheckout\u002Ftax.ts:6 copies backend\u002Fpricing\u002Ftax.ts:5 (similar)\nmoney: web\u002Fsrc\u002Floan\u002Fpayment.ts:4 copies backend\u002Floans\u002FInterest.kt:8 (semantic)\n",[276,1767,1765],{"__ignoreMap":298},[255,1769,1770],{},"The other reporters, SARIF, CodeClimate and HTML among them, don't list the copies, and the exit code fails the job all the same.",[262,1772,1774],{"id":1773},"in-ci","In CI",[255,1776,1777],{},"A rule fails the job through the exit code, so a step that runs jscpd on the repository root is the whole setup. Keep the jscpd cache between runs, or every run downloads the model again:",[293,1779,1784],{"className":1780,"code":1781,"filename":1782,"language":1783,"meta":298,"style":298},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","- uses: actions\u002Fcache@v4\n  with:\n    path: ~\u002F.cache\u002Fjscpd\n    key: jscpd-${{ runner.os }}-${{ github.run_id }}\n    restore-keys: jscpd-${{ runner.os }}-\n- run: npx jscpd --semantic-download\n- run: npx jscpd .\n",".github\u002Fworkflows\u002Fboundaries.yml","yaml",[276,1785,1786,1800,1808,1818,1828,1838,1850],{"__ignoreMap":298},[302,1787,1788,1791,1795,1797],{"class":304,"line":305},[302,1789,1790],{"class":308},"-",[302,1792,1794],{"class":1793},"swJcz"," uses",[302,1796,325],{"class":308},[302,1798,1799],{"class":354}," actions\u002Fcache@v4\n",[302,1801,1802,1805],{"class":304,"line":312},[302,1803,1804],{"class":1793},"  with",[302,1806,1807],{"class":308},":\n",[302,1809,1810,1813,1815],{"class":304,"line":331},[302,1811,1812],{"class":1793},"    path",[302,1814,325],{"class":308},[302,1816,1817],{"class":354}," ~\u002F.cache\u002Fjscpd\n",[302,1819,1820,1823,1825],{"class":304,"line":337},[302,1821,1822],{"class":1793},"    key",[302,1824,325],{"class":308},[302,1826,1827],{"class":354}," jscpd-${{ runner.os }}-${{ github.run_id }}\n",[302,1829,1830,1833,1835],{"class":304,"line":363},[302,1831,1832],{"class":1793},"    restore-keys",[302,1834,325],{"class":308},[302,1836,1837],{"class":354}," jscpd-${{ runner.os }}-\n",[302,1839,1840,1842,1845,1847],{"class":304,"line":397},[302,1841,1790],{"class":308},[302,1843,1844],{"class":1793}," run",[302,1846,325],{"class":308},[302,1848,1849],{"class":354}," npx jscpd --semantic-download\n",[302,1851,1852,1854,1856,1858],{"class":304,"line":420},[302,1853,1790],{"class":308},[302,1855,1844],{"class":1793},[302,1857,325],{"class":308},[302,1859,1860],{"class":354}," npx jscpd .\n",[255,1862,1863,1866,1867,1870,1871,1873],{},[276,1864,1865],{},"--semantic-download"," does nothing when the model is in the cache already. When every rule has ",[276,1868,1869],{},"\"disable\": [\"semantic\"]",", the job needs neither the download nor the cache. Violations don't go through a baseline, so a new rule fails on the copies that exist when you add it. Move them behind the boundary, or put them in ",[276,1872,425],{}," until you do.",[262,1875,1877],{"id":1876},"when-a-rule-checks-nothing","When a rule checks nothing",[255,1879,1880,1881,1286,1883,1885,1886,1888,1889,1891,1892,1894],{},"A rule that doesn't check what you meant must not look as if it passed. A mistake in a rule stops the run with exit code 1 and an error that names the rule: an unknown key, an empty ",[276,1882,276],{},[276,1884,402],{},", a glob that doesn't compile, a ",[276,1887,673],{}," with no method after it, a pattern of method names without parentheses, a threshold out of range, two rules with one name, an unknown kind in ",[276,1890,1162],{},", or a ",[276,1893,1162],{}," that turns off every kind.",[293,1896,1899],{"className":1897,"code":1898,"language":574,"meta":298},[572],"Error: boundaries: rule 'money': unknown field `notin`, expected one of `name`, `code`, `notIn`, `allow`, `thresholds`, `disable`\n",[276,1900,1898],{"__ignoreMap":298},[255,1902,1903,1904,1906],{},"A valid rule that checks nothing in a run gets a warning, ",[276,1905,1428],{}," or not, and the run goes on:",[293,1908,1911],{"className":1909,"code":1910,"language":574,"meta":298},[572],"Warning: boundaries: rule 'money' protects no code of this scan; check the paths and method names of its code, and the scan paths\nWarning: boundaries: rule 'money' keeps copies out of no file of this scan; check the paths of its notIn and allow, and the scan paths\nWarning: boundaries: rule 'money' looks for no kind of copy this run can find: renamed needs --ignore-identifiers, --ignore-literals or --ignore-annotations, gap needs --max-gap-lines\n",[276,1912,1910],{"__ignoreMap":298},[255,1914,1915,1916,1918,1919,1921,1922,1924,1925,1927,1928,1930,1931,1933],{},"The first comes from a typo in the paths or the method names of ",[276,1917,276],{},", or from a scan that leaves the protected code out. The second comes from a typo in ",[276,1920,402],{},", or from an ",[276,1923,425],{}," that takes every file ",[276,1926,402],{}," names. The third comes from a ",[276,1929,1162],{}," that leaves only kinds the run doesn't look for. The warnings cover a rule that checks nothing at all: a typo in one of several ",[276,1932,276],{}," entries leaves the others working and prints nothing. To test a new rule, copy a protected function into a place the rule keeps it out of and check that the run fails.",[262,1935,1937],{"id":1936},"limits","Limits",[1268,1939,1940,1950,1961,1988,1991],{},[1271,1941,1942,1943,522,1946,1949],{},"Violations don't go through ",[276,1944,1945],{},"--baseline",[276,1947,1948],{},"--changed"," doesn't narrow them.",[1271,1951,1952,515,1954,515,1956,662,1958,1960],{},[276,1953,1408],{},[276,1955,1411],{},[276,1957,1435],{},[276,1959,297],{}," show the violations. The other reporters, the language server and the MCP server don't.",[1271,1962,1963,1964,515,1966,515,1969,515,1972,515,1975,515,1978,662,1981,1984,1985,855],{},"Only a clone run checks the rules. ",[276,1965,1309],{},[276,1967,1968],{},"--dead-code",[276,1970,1971],{},"--complexity",[276,1973,1974],{},"--dashboard",[276,1976,1977],{},"--health",[276,1979,1980],{},"--mcp",[276,1982,1983],{},"--lsp"," don't, and neither do the commits of ",[276,1986,1987],{},"--history",[1271,1989,1990],{},"The rules read their files once more after the clone scan, and each rule runs its own search.",[1271,1992,1993],{},"jscpd finds only code that looks like the protected code. Money math in a frontend with no twin in the backend is a job for a linter.",[262,1995,1997],{"id":1996},"see-also","See also",[1268,1999,2000,2005,2010,2015,2020],{},[1271,2001,2002,2004],{},[270,2003,135],{"href":640},": the keys of a rule and their types.",[1271,2006,2007,2009],{},[270,2008,90],{"href":91},": the model, the cache and the thresholds of the semantic search.",[1271,2011,2012,2014],{},[270,2013,95],{"href":96},": how the structure search scores two functions.",[1271,2016,2017,2019],{},[270,2018,35],{"href":36},": the rest of a CI setup.",[1271,2021,2022,2024],{},[270,2023,140],{"href":141},": every gate that fails a run.",[2026,2027,2028],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":298,"searchDepth":312,"depth":312,"links":2030},[2031,2032,2039,2040,2041,2042,2043,2044,2045],{"id":264,"depth":312,"text":265},{"id":631,"depth":312,"text":632,"children":2033},[2034,2035,2036,2037,2038],{"id":652,"depth":331,"text":653},{"id":879,"depth":331,"text":880},{"id":1015,"depth":331,"text":1016},{"id":1034,"depth":331,"text":1035},{"id":1156,"depth":331,"text":1157},{"id":1262,"depth":312,"text":1263},{"id":1346,"depth":312,"text":1347},{"id":1402,"depth":312,"text":1403},{"id":1773,"depth":312,"text":1774},{"id":1876,"depth":312,"text":1877},{"id":1936,"depth":312,"text":1937},{"id":1996,"depth":312,"text":1997},"Keep the code of a module, a folder, a file or a method inside its boundary, and fail the run when a copy shows up elsewhere: copied, edited or written again in another language.","md",null,{},{"icon":103},{"title":2052,"description":2053},"Keep Code Inside Its Module with jscpd Boundaries","Boundary rules in .jscpd.json name protected code and the places its copies must stay out of. jscpd finds copy-paste, edited copies and the same logic in another language, and exits with code 1 on a copy.","13eynrQNcM1bzgboBfCMKh-saDGL6FYsBKSAnDAMTEA",[2056,2058],{"title":95,"path":96,"stem":97,"description":2057,"icon":98,"children":-1},"Find functions with the same structure in 15 languages, renamed copies and lightly edited ones included, with jscpd --similarity.",{"title":110,"path":111,"stem":112,"description":2059,"icon":113,"children":-1},"Exact, renamed, near-miss and semantic clones, and which of them jscpd finds.",{"\u002Fguides\u002Fboundaries":2061},{"views":2062,"visits":2062},0,1792421155031]