jscpd-rs 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/Cargo.lock +1 -1
- package/Cargo.toml +2 -1
- package/README.md +29 -5
- package/docs/migrating-from-jscpd.md +2 -2
- package/docs/npm-release.md +30 -26
- package/docs/prebuilt-binaries.md +18 -10
- package/docs/public-benchmark-suite.md +5 -1
- package/docs/release-checklist.md +17 -10
- package/docs/release-decisions.md +7 -1
- package/docs/release-readiness.md +6 -3
- package/docs/user-guide.md +1 -1
- package/npm/prebuilt-targets.json +3 -3
- package/package.json +6 -6
- package/src/bin/jscpd-server.rs +1 -1
- package/src/blame.rs +116 -0
- package/src/cli/config.rs +200 -0
- package/src/detector/matching/secondary.rs +0 -1
- package/src/detector/model.rs +4 -3
- package/src/detector/prepare.rs +12 -6
- package/src/detector.rs +25 -19
- package/src/files/discovery.rs +49 -45
- package/src/files/gitignore.rs +80 -0
- package/src/files/shebang.rs +61 -0
- package/src/files/test_support.rs +21 -0
- package/src/files/tests.rs +112 -13
- package/src/files.rs +3 -0
- package/src/formats.rs +216 -212
- package/src/lib.rs +18 -250
- package/src/main.rs +4 -4
- package/src/server.rs +10 -8
- package/src/tokenizer/blocks.rs +20 -24
- package/src/tokenizer/oxc/jsx.rs +77 -0
- package/src/verbose.rs +60 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.3 - 2026-06-01
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Tighten GitHub Release publication gates: npm and crates.io publication now
|
|
8
|
+
run the release-candidate gate before publishing.
|
|
9
|
+
- Block the main npm package publication if any configured prebuilt platform
|
|
10
|
+
package is missing or failed to publish.
|
|
11
|
+
- Add a core coverage gate to the release-candidate flow.
|
|
12
|
+
- Add an advisory server benchmark for comparing native and upstream
|
|
13
|
+
`/api/check` latency.
|
|
14
|
+
- Refresh npm, prebuilt-binary, release-readiness, and README documentation for
|
|
15
|
+
the prebuilt-first install path.
|
|
16
|
+
|
|
17
|
+
## 0.1.2 - 2026-06-01
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Rename the Windows prebuilt npm package to `jscpd-rs-win` to avoid npm
|
|
22
|
+
registry spam-policy false positives on the previous machine-generated name.
|
|
23
|
+
- Move the Linux arm64 prebuilt build to the Ubuntu 22.04 ARM runner for a more
|
|
24
|
+
stable native ARM publication path and older glibc baseline.
|
|
25
|
+
- Allow npm release workflow reruns for a single prebuilt target without
|
|
26
|
+
republishing the already-published main package.
|
|
27
|
+
|
|
3
28
|
## 0.1.1 - 2026-06-01
|
|
4
29
|
|
|
5
30
|
### Added
|
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ npx jscpd-rs .
|
|
|
48
48
|
Current npm packaging note: `jscpd-rs` installs prebuilt Linux, macOS, and
|
|
49
49
|
Windows binaries where available, then falls back to building from source with
|
|
50
50
|
Cargo for unsupported platforms. The original `0.1.0` npm package was
|
|
51
|
-
source-build only; use `0.1.
|
|
51
|
+
source-build only; use `0.1.2+` for the full prebuilt-first path. See
|
|
52
52
|
[docs/prebuilt-binaries.md](docs/prebuilt-binaries.md).
|
|
53
53
|
|
|
54
54
|
From this repository:
|
|
@@ -138,7 +138,6 @@ jobs:
|
|
|
138
138
|
runs-on: ubuntu-latest
|
|
139
139
|
steps:
|
|
140
140
|
- uses: actions/checkout@v5
|
|
141
|
-
- uses: dtolnay/rust-toolchain@stable
|
|
142
141
|
- uses: actions/setup-node@v5
|
|
143
142
|
with:
|
|
144
143
|
node-version: 22
|
|
@@ -235,11 +234,20 @@ Latest recorded public benchmark baseline for duplicate-code detection:
|
|
|
235
234
|
Reproduce the public benchmark and coverage suite:
|
|
236
235
|
|
|
237
236
|
```bash
|
|
238
|
-
|
|
237
|
+
scripts/release-candidate.sh
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Benchmark native server snippet checks against upstream:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
RUNS=20 scripts/bench-server.sh
|
|
239
244
|
```
|
|
240
245
|
|
|
241
246
|
Release-candidate workflows rerun the public suite before each new publication
|
|
242
|
-
so README numbers stay tied to a concrete
|
|
247
|
+
and enforce the core coverage gate, so README numbers stay tied to a concrete
|
|
248
|
+
commit and gate output. The public release gate fails below a 45x speedup on
|
|
249
|
+
the default benchmark cases to prevent silent performance regressions while
|
|
250
|
+
preserving room for normal runner noise.
|
|
243
251
|
|
|
244
252
|
## Library API
|
|
245
253
|
|
|
@@ -319,6 +327,19 @@ scripts/compat-reporters.sh
|
|
|
319
327
|
STRICT=coverage scripts/compat-matrix.sh
|
|
320
328
|
```
|
|
321
329
|
|
|
330
|
+
Rust code coverage is optional and intentionally kept out of the default fast
|
|
331
|
+
gate:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
cargo install cargo-llvm-cov --locked
|
|
335
|
+
SUMMARY=1 scripts/coverage.sh
|
|
336
|
+
SCOPE=core SUMMARY=1 scripts/coverage.sh
|
|
337
|
+
SCOPE=core FAIL_UNDER_LINES=93 scripts/coverage.sh
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Black-box behavior tests that exercise the public API live in `tests/`. Small
|
|
341
|
+
private-helper tests stay next to the module they protect.
|
|
342
|
+
|
|
322
343
|
Known upstream bug candidates and intentional compatibility exceptions are
|
|
323
344
|
tracked in [docs/upstream-bugs.md](docs/upstream-bugs.md). GitHub-ready issue
|
|
324
345
|
drafts are prepared in
|
|
@@ -332,6 +353,9 @@ Fast local gate:
|
|
|
332
353
|
scripts/release-gate.sh
|
|
333
354
|
```
|
|
334
355
|
|
|
356
|
+
The fast gate includes `cargo fmt`, `cargo test`, shell syntax checks,
|
|
357
|
+
`shellcheck`, package/install checks, and the focused compatibility gates.
|
|
358
|
+
|
|
335
359
|
Package/install gate:
|
|
336
360
|
|
|
337
361
|
```bash
|
|
@@ -353,7 +377,7 @@ FULL=1 scripts/release-gate.sh
|
|
|
353
377
|
Public benchmark and coverage gate:
|
|
354
378
|
|
|
355
379
|
```bash
|
|
356
|
-
|
|
380
|
+
scripts/release-candidate.sh
|
|
357
381
|
```
|
|
358
382
|
|
|
359
383
|
Release candidate gate:
|
|
@@ -127,7 +127,7 @@ These are intentional first-release limits:
|
|
|
127
127
|
tokenizer is needed;
|
|
128
128
|
- the Rust crate exposes a native Rust API, not the upstream JavaScript package
|
|
129
129
|
API;
|
|
130
|
-
- `jscpd-rs@0.1.
|
|
130
|
+
- `jscpd-rs@0.1.2+` npm packaging uses prebuilt binaries on supported Linux,
|
|
131
131
|
macOS, and Windows targets, with a Cargo source-build fallback for
|
|
132
132
|
unsupported platforms. The original `0.1.0` npm package was source-build
|
|
133
133
|
only.
|
|
@@ -192,7 +192,7 @@ npm/npx-based `jscpd-rs` CI:
|
|
|
192
192
|
- run: npx jscpd-rs src --reporters console,json --threshold 5 --exitCode 1
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
`jscpd-rs@0.1.
|
|
195
|
+
`jscpd-rs@0.1.2+` npm installs use prebuilt binaries where available and fall
|
|
196
196
|
back to Cargo source-build on unsupported platforms. The original `0.1.0` npm
|
|
197
197
|
package still needs Rust available during installation; see the
|
|
198
198
|
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
package/docs/npm-release.md
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
Current npm readiness snapshot:
|
|
4
4
|
|
|
5
5
|
- Date: 2026-06-01.
|
|
6
|
-
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
`
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
- Current published version: `jscpd-rs@0.1.3`.
|
|
7
|
+
- Latest npm publish workflow: `v0.1.3` GitHub Release workflow.
|
|
8
|
+
- Published platform packages: `jscpd-rs-linux-x64-gnu`,
|
|
9
|
+
`jscpd-rs-linux-arm64-gnu`, `jscpd-rs-darwin-x64`,
|
|
10
|
+
`jscpd-rs-darwin-arm64`, and `jscpd-rs-win`.
|
|
11
|
+
- Post-publication smoke passed from clean temporary directories:
|
|
12
|
+
`npm install jscpd-rs@0.1.3`, `jscpd-rs --version`, `jscpd --version`,
|
|
13
|
+
`jscpd-server --version`, and
|
|
14
|
+
`npx --package jscpd-rs@0.1.3 jscpd-rs --version`.
|
|
15
|
+
- Rerun `scripts/npm-package-check.sh` on the exact checkout before publishing
|
|
16
|
+
any new npm version.
|
|
15
17
|
|
|
16
18
|
The npm package is `jscpd-rs`. It exposes these bin commands:
|
|
17
19
|
|
|
@@ -19,7 +21,7 @@ The npm package is `jscpd-rs`. It exposes these bin commands:
|
|
|
19
21
|
- `jscpd`: installed alias for the native `jscpd` CLI.
|
|
20
22
|
- `jscpd-server`: installed alias for the native server binary.
|
|
21
23
|
|
|
22
|
-
`jscpd-rs@0.1.
|
|
24
|
+
`jscpd-rs@0.1.2+` publishes prebuilt platform packages before the main
|
|
23
25
|
`jscpd-rs` package. The CLI behavior stays the same, and the source-build path
|
|
24
26
|
remains the fallback for unsupported platforms. The original `0.1.0` package
|
|
25
27
|
was source-build only; see the
|
|
@@ -57,7 +59,7 @@ Before actual publication, run:
|
|
|
57
59
|
git status --short
|
|
58
60
|
npm whoami
|
|
59
61
|
scripts/npm-package-check.sh
|
|
60
|
-
npm view jscpd-rs version
|
|
62
|
+
npm view jscpd-rs@X.Y.Z version
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
For a new version, `npm view jscpd-rs@X.Y.Z version` should fail before the
|
|
@@ -70,14 +72,8 @@ crate and GitHub tag have already been published: that script is the full
|
|
|
70
72
|
Cargo/GitHub first-publication gate and intentionally checks that the crate name
|
|
71
73
|
and release tag are still available.
|
|
72
74
|
|
|
73
|
-
If
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
scripts/npm-package-check.sh
|
|
77
|
-
npm publish --access public
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
If the account requires a one-time password for publish:
|
|
75
|
+
If an emergency manual fallback is approved and the npm account requires a
|
|
76
|
+
one-time password for publish:
|
|
81
77
|
|
|
82
78
|
```bash
|
|
83
79
|
npm publish --access public --otp 123456
|
|
@@ -97,11 +93,17 @@ This repository provides a publish workflow:
|
|
|
97
93
|
```
|
|
98
94
|
|
|
99
95
|
The workflow runs automatically when a non-draft, non-prerelease GitHub Release
|
|
100
|
-
is published. It checks out the release tag,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
is published. It checks out the release tag, runs `scripts/release-candidate.sh`
|
|
97
|
+
as a preflight, verifies that the tag matches the `package.json` version,
|
|
98
|
+
verifies that the main npm version is not already published, builds platform
|
|
99
|
+
packages in a native runner matrix, publishes those platform packages first,
|
|
100
|
+
verifies that every optional platform package exists for the release version,
|
|
101
|
+
runs `scripts/npm-package-check.sh`, and then publishes the main `jscpd-rs`
|
|
102
|
+
package.
|
|
103
|
+
|
|
104
|
+
The main package is blocked when any configured platform package fails or is
|
|
105
|
+
missing. For a target-only rerun, set `publish_main=false`; using `target` with
|
|
106
|
+
`publish_main=true` is rejected by the workflow.
|
|
105
107
|
|
|
106
108
|
It can also be run manually from GitHub Actions as a fallback by entering
|
|
107
109
|
`jscpd-rs` in the confirmation input.
|
|
@@ -127,13 +129,15 @@ Then publish automatically from GitHub:
|
|
|
127
129
|
4. Create and publish a GitHub Release with tag `vX.Y.Z`.
|
|
128
130
|
5. GitHub Actions will run `npm-publish` from that release tag.
|
|
129
131
|
|
|
130
|
-
Manual fallback:
|
|
132
|
+
Manual workflow fallback:
|
|
131
133
|
|
|
132
134
|
1. Open GitHub Actions.
|
|
133
135
|
2. Select the `npm-publish` workflow.
|
|
134
136
|
3. Click **Run workflow** on `main`.
|
|
135
137
|
4. Enter `jscpd-rs` for `package_name`.
|
|
136
|
-
5.
|
|
138
|
+
5. Optionally enter a single target key such as `linux-arm64-gnu` and set
|
|
139
|
+
`publish_main=false` when only one prebuilt package needs a rerun.
|
|
140
|
+
6. Run the workflow.
|
|
137
141
|
|
|
138
142
|
If npm does not allow Trusted Publishing configuration before a platform package
|
|
139
143
|
version exists, add a short-lived `NPM_TOKEN` GitHub secret for the first
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Prebuilt Binary Distribution
|
|
2
2
|
|
|
3
|
-
`jscpd-rs` is a native Rust CLI. Starting with `jscpd-rs@0.1.
|
|
3
|
+
`jscpd-rs` is a native Rust CLI. Starting with `jscpd-rs@0.1.2`, npm
|
|
4
4
|
distribution uses a small main package plus platform-specific optional
|
|
5
5
|
packages. The original `0.1.0` npm package was source-build only.
|
|
6
6
|
|
|
@@ -28,10 +28,10 @@ The target matrix is defined in `npm/prebuilt-targets.json`.
|
|
|
28
28
|
| Package | Rust target | Runner |
|
|
29
29
|
| --- | --- | --- |
|
|
30
30
|
| `jscpd-rs-linux-x64-gnu` | `x86_64-unknown-linux-gnu` | `ubuntu-24.04` |
|
|
31
|
-
| `jscpd-rs-linux-arm64-gnu` | `aarch64-unknown-linux-gnu` | `ubuntu-
|
|
31
|
+
| `jscpd-rs-linux-arm64-gnu` | `aarch64-unknown-linux-gnu` | `ubuntu-22.04-arm` |
|
|
32
32
|
| `jscpd-rs-darwin-x64` | `x86_64-apple-darwin` | `macos-15-intel` |
|
|
33
33
|
| `jscpd-rs-darwin-arm64` | `aarch64-apple-darwin` | `macos-15` |
|
|
34
|
-
| `jscpd-rs-
|
|
34
|
+
| `jscpd-rs-win` | `x86_64-pc-windows-msvc` | `windows-2025` |
|
|
35
35
|
|
|
36
36
|
Consider Linux musl and Windows arm64 only after install data or user reports
|
|
37
37
|
show demand.
|
|
@@ -54,12 +54,20 @@ show demand.
|
|
|
54
54
|
The GitHub Release workflow in `.github/workflows/npm-publish.yml` publishes in
|
|
55
55
|
this order:
|
|
56
56
|
|
|
57
|
-
1.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
1. Run `scripts/release-candidate.sh`, including the full compatibility
|
|
58
|
+
matrix, public speed gate, and core coverage gate.
|
|
59
|
+
2. Verify that the GitHub Release tag matches `package.json`.
|
|
60
|
+
3. Build platform packages in a native runner matrix.
|
|
61
|
+
4. Smoke-test `jscpd --version` and `jscpd-server --version` for each package.
|
|
62
|
+
5. Publish the platform packages.
|
|
63
|
+
6. Verify every optional platform package is published for the release version.
|
|
64
|
+
7. Run `scripts/npm-package-check.sh`.
|
|
65
|
+
8. Publish the main `jscpd-rs` package.
|
|
66
|
+
|
|
67
|
+
The main npm package is intentionally blocked when any configured platform
|
|
68
|
+
package fails or is missing. Manual target-only reruns must set
|
|
69
|
+
`publish_main=false`; the main package should not be published with an
|
|
70
|
+
accidental source-build fallback on a supported platform.
|
|
63
71
|
|
|
64
72
|
The workflow publishes with npm provenance enabled. It is designed for Trusted
|
|
65
73
|
Publishing, but `NPM_TOKEN` can be kept as a temporary bootstrap fallback for
|
|
@@ -83,7 +91,7 @@ Packages:
|
|
|
83
91
|
- `jscpd-rs-linux-arm64-gnu`
|
|
84
92
|
- `jscpd-rs-darwin-x64`
|
|
85
93
|
- `jscpd-rs-darwin-arm64`
|
|
86
|
-
- `jscpd-rs-
|
|
94
|
+
- `jscpd-rs-win`
|
|
87
95
|
|
|
88
96
|
If a temporary npm token is used for the first platform-package bootstrap,
|
|
89
97
|
revoke it after Trusted Publishing succeeds.
|
|
@@ -31,7 +31,7 @@ Usage:
|
|
|
31
31
|
LIST=1 scripts/public-bench-suite.sh
|
|
32
32
|
CASES=react,next RUNS=3 scripts/public-bench-suite.sh
|
|
33
33
|
CHECK_COMPAT=1 CASES=react scripts/public-bench-suite.sh
|
|
34
|
-
MIN_SPEEDUP=
|
|
34
|
+
MIN_SPEEDUP=45 CASES=react,next RUNS=3 scripts/public-bench-suite.sh
|
|
35
35
|
UPSTREAM_TIMEOUT=600s CASES=vscode RUNS=1 scripts/public-bench-suite.sh
|
|
36
36
|
PUBLIC=1 PUBLIC_CASES=react,next PUBLIC_RUNS=3 scripts/release-gate.sh
|
|
37
37
|
```
|
|
@@ -47,6 +47,10 @@ bounded by `UPSTREAM_TIMEOUT` (`600s` by default) so optional stress cases canno
|
|
|
47
47
|
hang a release gate indefinitely; set `RUST_TIMEOUT` or `UPSTREAM_TIMEOUT` to an
|
|
48
48
|
empty value to disable that side's timeout.
|
|
49
49
|
|
|
50
|
+
The release gate uses `PUBLIC_MIN_SPEEDUP=45` by default. This intentionally
|
|
51
|
+
locks the 50x+ product baseline with enough headroom for normal runner noise;
|
|
52
|
+
lowering that threshold requires an explicit release decision.
|
|
53
|
+
|
|
50
54
|
When `CHECK_COMPAT=1` is enabled, the suite runs the same coverage-first report
|
|
51
55
|
comparison used by the fixture gates. `react`, `next`, and `prometheus` include
|
|
52
56
|
narrow allowlists for upstream overextended ranges documented in
|
|
@@ -79,6 +79,8 @@ Before publishing, all of these must be true:
|
|
|
79
79
|
- `git submodule status jscpd` points at the reviewed upstream reference.
|
|
80
80
|
- `scripts/release-candidate.sh` passes on the exact code commit being tagged.
|
|
81
81
|
- GitHub Actions `release-gate` passes on the pushed commit.
|
|
82
|
+
- GitHub Actions `crates-publish` and `npm-publish` release jobs are allowed to
|
|
83
|
+
run their own release-candidate preflight before publishing.
|
|
82
84
|
- `scripts/package-check.sh` passes and the package file list excludes
|
|
83
85
|
`jscpd/`, `target/`, `node_modules/`, and `scripts/`.
|
|
84
86
|
- `scripts/npm-package-check.sh` passes, including `npm pack`,
|
|
@@ -89,9 +91,12 @@ Before publishing, all of these must be true:
|
|
|
89
91
|
- `README.md`, `docs/compat-baseline.md`, and
|
|
90
92
|
`docs/public-benchmark-suite.md` contain the same recorded public benchmark
|
|
91
93
|
numbers.
|
|
92
|
-
- For
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
- For a new publication, the target crate version, npm package versions, and
|
|
95
|
+
`vX.Y.Z` Git tag are not already published, unless an explicitly documented
|
|
96
|
+
target-only npm rerun is being used for a prebuilt package.
|
|
97
|
+
- For npm publication, every configured prebuilt optional package is published
|
|
98
|
+
before the main `jscpd-rs` package; target-only reruns use
|
|
99
|
+
`publish_main=false`.
|
|
95
100
|
- `docs/upstream-bugs.md` contains concrete repro commands for upstream issues
|
|
96
101
|
we plan to file.
|
|
97
102
|
- `docs/upstream-issue-drafts.md` contains reviewed issue drafts ready to
|
|
@@ -140,18 +145,20 @@ scripts/prepublish-check.sh
|
|
|
140
145
|
```
|
|
141
146
|
|
|
142
147
|
The script checks clean git state, the reviewed `jscpd` submodule reference,
|
|
143
|
-
local and remote tag availability, exact crate
|
|
144
|
-
`cargo
|
|
148
|
+
local and remote tag availability, exact crate version availability through
|
|
149
|
+
`cargo info`, exact npm package version availability through `npm view`,
|
|
145
150
|
benchmark-number consistency across release docs, the full release-candidate
|
|
146
151
|
gate, package/install validation, npm pack/npx validation, and
|
|
147
|
-
`cargo publish --dry-run --locked`.
|
|
148
|
-
|
|
152
|
+
`cargo publish --dry-run --locked`. The npm availability check covers the main
|
|
153
|
+
package and every prebuilt optional package. Set
|
|
154
|
+
`RUN_RELEASE_CANDIDATE=0` only when the same code commit already has fresh
|
|
155
|
+
local and CI release-candidate evidence.
|
|
149
156
|
|
|
150
157
|
Then push the exact release commit and verify the GitHub Actions
|
|
151
158
|
`release-gate` result. Use the workflow dispatch `release_candidate` input for a
|
|
152
159
|
full CI-side release-candidate run when needed.
|
|
153
160
|
|
|
154
|
-
|
|
161
|
+
Historical first publication candidate checked on 2026-05-31: local and remote
|
|
155
162
|
`v0.1.0` tag lookups returned no entries. `cargo search jscpd-rs --limit 5`
|
|
156
163
|
returned no exact crate, `npm view jscpd-rs version` returned `E404`, and the
|
|
157
164
|
sparse crates.io index path
|
|
@@ -178,8 +185,8 @@ two explicit commands above are a manual smoke equivalent for post-tag checks.
|
|
|
178
185
|
Track these after the first release candidate:
|
|
179
186
|
|
|
180
187
|
- Reduce noisy extra Rust findings where they are user-visible false positives.
|
|
181
|
-
-
|
|
182
|
-
|
|
188
|
+
- Monitor npm prebuilt install behavior and add Linux musl or Windows arm64
|
|
189
|
+
packages only when install data or user reports show demand; see the
|
|
183
190
|
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
|
184
191
|
- Add native persistent store/cache only if release-scale benchmark data needs
|
|
185
192
|
it.
|
|
@@ -77,8 +77,14 @@ choice insufficient.
|
|
|
77
77
|
|
|
78
78
|
- Performance remains a product requirement. Public benchmark runs should be
|
|
79
79
|
repeated before publication with pinned commits and recorded speedups.
|
|
80
|
+
- Refactors, simplifications, compatibility fixes, and dependency swaps must not
|
|
81
|
+
introduce sustained speed regressions. If a change touches discovery,
|
|
82
|
+
tokenization, matching, source loading, reporter hot paths, or server snippet
|
|
83
|
+
checks, rerun the affected benchmark case before treating the core as stable.
|
|
80
84
|
- The aspirational target is 50x on representative cases, but release gating
|
|
81
|
-
should use measured thresholds from the selected public benchmark suite.
|
|
85
|
+
should use measured thresholds from the selected public benchmark suite. The
|
|
86
|
+
default public release gate currently fails below 45x to preserve the 50x+
|
|
87
|
+
baseline while leaving room for runner noise.
|
|
82
88
|
|
|
83
89
|
## Approved Complex Feature Choices
|
|
84
90
|
|
|
@@ -23,7 +23,9 @@ current implementation status.
|
|
|
23
23
|
| Native Rust API | ready | `jscpd`, `jscpd_with_exit_callback`, `Tokenizer`, `Detector`, `Statistic`, `MemoryStore`, `detect_clones`, `detect_clones_and_statistic`, `detect_clones_and_statistics`, `detect_source_files`, default options, argv option parsing, supported formats, and format lookup helpers expose the app, tokenizer, detector, statistics, and store core for path-based and in-memory integrations. See `docs/api-parity.md`. |
|
|
24
24
|
| Native server | partial | `jscpd-server` exposes `/`, `/api/health`, `/api/stats`, `/api/check`, `/api/recheck`, and `/mcp`; exact help text, stable CLI, HTTP success/error, and MCP contracts are gated; `/api/check` reuses prepared project token maps; exact upstream Streamable HTTP SDK behavior remains follow-up. |
|
|
25
25
|
| Performance harness | ready | Local benchmark script and public benchmark suite with pinned output recording and speedup gates. |
|
|
26
|
-
|
|
|
26
|
+
| Server benchmark harness | ready | `scripts/bench-server.sh` compares native and upstream `/api/check` latency on the same initialized project and snippet payload. Keep it advisory until real server usage needs a blocking gate. |
|
|
27
|
+
| Release gates | ready | Default CI gate, full compatibility matrix, Cargo/npm package checks, reporter/config/CLI/blame gates. The default gate prints per-step timings, caches Cargo/pnpm/upstream build artifacts, and uses target-reuse npm package smoke in push/PR CI while keeping cold npm source-build in release-candidate/prepublish gates. GitHub npm/crates publish workflows now run `scripts/release-candidate.sh` before publishing. |
|
|
28
|
+
| Code coverage tooling | ready | One script: `scripts/coverage.sh`. Use `SCOPE=full` for the full advisory report and `SCOPE=core` for core coverage that runs all test targets while excluding CLI/server glue from the report. Local baseline on 2026-06-01: full 91.54% line / 90.13% region coverage; core 93.18% line / 91.39% region coverage. `scripts/release-candidate.sh` enforces `SCOPE=core FAIL_UNDER_LINES=93 scripts/coverage.sh` by default. |
|
|
27
29
|
|
|
28
30
|
## Partial Or Follow-Up
|
|
29
31
|
|
|
@@ -35,8 +37,8 @@ current implementation status.
|
|
|
35
37
|
| HTML reporter polish | practical parity | Keep self-contained HTML stable. Do not chase pixel-perfect upstream parity for the first release. |
|
|
36
38
|
| Terminal cosmetics | practical parity | Important messages are gated; exact wrapping/order remains lower priority. |
|
|
37
39
|
| Upstream JavaScript API parity | follow-up | Native Rust helpers cover the practical app/tokenizer/detector/statistics/store concepts, including an embeddable argv runner and tokenizer map generation; exact JS package export shape is not implemented in the Rust crate. See `docs/api-parity.md`. |
|
|
38
|
-
| Server snippet matching | optimized baseline | Native `/api/check` and MCP `check_duplication` are functional and reuse project token maps from the last scan;
|
|
39
|
-
| Npm prebuilt binaries | ready
|
|
40
|
+
| Server snippet matching | optimized baseline | Native `/api/check` and MCP `check_duplication` are functional and reuse project token maps from the last scan; use `scripts/bench-server.sh` before adding a dedicated window index. |
|
|
41
|
+
| Npm prebuilt binaries | ready | Platform-specific optional package metadata, runtime prebuilt resolution, prebuilt/fallback package checks, and GitHub Release publishing automation are wired for `jscpd-rs@0.1.2+`. The main npm package is blocked if any configured platform package fails or is missing. See the [prebuilt binary distribution plan](prebuilt-binaries.md). |
|
|
40
42
|
| Latest full publication gate | ready | `scripts/prepublish-check.sh` passed locally on code commit `8c3da0e`, including `scripts/release-candidate.sh`, package/install verification, crate/tag availability checks, npm package/name/npx verification, and `cargo publish --dry-run --locked`. GitHub Actions default `release-gate` passed on code commit `8c3da0e` in run `26710762680`. After benchmark documentation updates, `RUN_RELEASE_CANDIDATE=0 scripts/prepublish-check.sh` is the package/dry-run refresh gate for the exact package contents being tagged. |
|
|
41
43
|
|
|
42
44
|
## Post-MVP
|
|
@@ -49,3 +51,4 @@ current implementation status.
|
|
|
49
51
|
| MCP endpoint polish | Core native endpoint exists; tighten exact SDK edge cases only when MCP client compatibility demands it. |
|
|
50
52
|
| Persistent cache/store backends | Add only if public benchmark data proves the in-memory path is insufficient. |
|
|
51
53
|
| Full Prism grammar port | Do not rewrite all grammars eagerly; use native crates or small scanners only for proven gaps. |
|
|
54
|
+
| CI coverage enforcement | Keep coverage out of the default fast gate until CI runtime is measured; the core coverage gate now runs in release-candidate flows. |
|
package/docs/user-guide.md
CHANGED
|
@@ -29,7 +29,7 @@ npm install -g jscpd-rs
|
|
|
29
29
|
npx jscpd-rs --version
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`jscpd-rs@0.1.
|
|
32
|
+
`jscpd-rs@0.1.2+` installs prebuilt Linux, macOS, and Windows binaries where
|
|
33
33
|
available and falls back to building from source with Cargo for unsupported
|
|
34
34
|
platforms. The original `0.1.0` npm package was source-build only. See the
|
|
35
35
|
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"cpu": "arm64",
|
|
16
16
|
"libc": "glibc",
|
|
17
17
|
"rustTarget": "aarch64-unknown-linux-gnu",
|
|
18
|
-
"runner": "ubuntu-
|
|
18
|
+
"runner": "ubuntu-22.04-arm"
|
|
19
19
|
},
|
|
20
20
|
"darwin-x64": {
|
|
21
21
|
"packageName": "jscpd-rs-darwin-x64",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"runner": "macos-15"
|
|
35
35
|
},
|
|
36
36
|
"win32-x64-msvc": {
|
|
37
|
-
"packageName": "jscpd-rs-
|
|
38
|
-
"description": "Prebuilt Windows x64
|
|
37
|
+
"packageName": "jscpd-rs-win",
|
|
38
|
+
"description": "Prebuilt Windows x64 binaries for jscpd-rs",
|
|
39
39
|
"os": "win32",
|
|
40
40
|
"cpu": "x64",
|
|
41
41
|
"rustTarget": "x86_64-pc-windows-msvc",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jscpd-rs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "50x+ faster duplicate-code detector for CI/CD; jscpd-compatible CLI, SARIF, JSON, HTML reports",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"cli"
|
|
29
29
|
],
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"jscpd-rs-darwin-arm64": "0.1.
|
|
32
|
-
"jscpd-rs-darwin-x64": "0.1.
|
|
33
|
-
"jscpd-rs-linux-arm64-gnu": "0.1.
|
|
34
|
-
"jscpd-rs-linux-x64-gnu": "0.1.
|
|
35
|
-
"jscpd-rs-
|
|
31
|
+
"jscpd-rs-darwin-arm64": "0.1.3",
|
|
32
|
+
"jscpd-rs-darwin-x64": "0.1.3",
|
|
33
|
+
"jscpd-rs-linux-arm64-gnu": "0.1.3",
|
|
34
|
+
"jscpd-rs-linux-x64-gnu": "0.1.3",
|
|
35
|
+
"jscpd-rs-win": "0.1.3"
|
|
36
36
|
},
|
|
37
37
|
"bin": {
|
|
38
38
|
"jscpd-rs": "npm/bin/jscpd-rs.js",
|
package/src/bin/jscpd-server.rs
CHANGED
package/src/blame.rs
CHANGED
|
@@ -94,6 +94,10 @@ fn blame_line_regex() -> &'static Regex {
|
|
|
94
94
|
#[cfg(test)]
|
|
95
95
|
mod tests {
|
|
96
96
|
use super::*;
|
|
97
|
+
use crate::tokenizer::Location;
|
|
98
|
+
use std::fs;
|
|
99
|
+
use std::path::{Path, PathBuf};
|
|
100
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
97
101
|
|
|
98
102
|
#[test]
|
|
99
103
|
fn parses_git_blame_lines() {
|
|
@@ -127,4 +131,116 @@ cccccccc (Carol 2024-01-03 00:00:00 +0000 3) third
|
|
|
127
131
|
assert_eq!(sliced["2"].author, "Bob");
|
|
128
132
|
assert_eq!(sliced["3"].author, "Carol");
|
|
129
133
|
}
|
|
134
|
+
|
|
135
|
+
#[test]
|
|
136
|
+
fn ignores_malformed_git_blame_lines() {
|
|
137
|
+
let blame = parse_git_blame(
|
|
138
|
+
"\
|
|
139
|
+
not blame output
|
|
140
|
+
aaaaaaaa (Alice 2024-01-01 00:00:00 +0000 1) first
|
|
141
|
+
bbbbbbbb (Bob 2024-01-02 00:00 +0000 2) bad date
|
|
142
|
+
",
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
assert_eq!(blame.len(), 1);
|
|
146
|
+
assert_eq!(blame["1"].author, "Alice");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#[test]
|
|
150
|
+
fn applies_cached_blame_to_fragment_range() {
|
|
151
|
+
let mut cache = HashMap::from([(
|
|
152
|
+
"src/a.js".to_string(),
|
|
153
|
+
Some(parse_git_blame(
|
|
154
|
+
"\
|
|
155
|
+
aaaaaaaa (Alice 2024-01-01 00:00:00 +0000 1) first
|
|
156
|
+
bbbbbbbb (Bob 2024-01-02 00:00:00 +0000 2) second
|
|
157
|
+
cccccccc (Carol 2024-01-03 00:00:00 +0000 3) third
|
|
158
|
+
",
|
|
159
|
+
)),
|
|
160
|
+
)]);
|
|
161
|
+
let mut fragment = fragment("src/a.js", 2, 3);
|
|
162
|
+
|
|
163
|
+
apply_fragment_blame(&mut fragment, &mut cache);
|
|
164
|
+
|
|
165
|
+
let blame = fragment.blame.expect("fragment blame");
|
|
166
|
+
assert_eq!(blame.keys().cloned().collect::<Vec<_>>(), vec!["2", "3"]);
|
|
167
|
+
assert_eq!(blame["2"].author, "Bob");
|
|
168
|
+
assert_eq!(blame["3"].author, "Carol");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#[test]
|
|
172
|
+
fn omits_cached_blame_when_file_or_range_has_no_blame() {
|
|
173
|
+
let mut cache = HashMap::from([
|
|
174
|
+
("missing.js".to_string(), None),
|
|
175
|
+
(
|
|
176
|
+
"src/a.js".to_string(),
|
|
177
|
+
Some(parse_git_blame(
|
|
178
|
+
"aaaaaaaa (Alice 2024-01-01 00:00:00 +0000 10) tenth\n",
|
|
179
|
+
)),
|
|
180
|
+
),
|
|
181
|
+
]);
|
|
182
|
+
let mut missing = fragment("missing.js", 1, 1);
|
|
183
|
+
let mut outside_range = fragment("src/a.js", 1, 2);
|
|
184
|
+
|
|
185
|
+
apply_fragment_blame(&mut missing, &mut cache);
|
|
186
|
+
apply_fragment_blame(&mut outside_range, &mut cache);
|
|
187
|
+
|
|
188
|
+
assert!(missing.blame.is_none());
|
|
189
|
+
assert!(outside_range.blame.is_none());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
#[test]
|
|
193
|
+
fn reads_git_blame_for_tracked_file() {
|
|
194
|
+
let repo = unique_temp_dir("blame-repo");
|
|
195
|
+
fs::create_dir_all(&repo).unwrap();
|
|
196
|
+
git(&repo, &["init", "-q"]);
|
|
197
|
+
git(&repo, &["config", "user.email", "jscpd-rs@example.test"]);
|
|
198
|
+
git(&repo, &["config", "user.name", "Jscpd Rs"]);
|
|
199
|
+
let path = repo.join("tracked.js");
|
|
200
|
+
fs::write(&path, "const first = 1;\nconst second = 2;\n").unwrap();
|
|
201
|
+
git(&repo, &["add", "tracked.js"]);
|
|
202
|
+
git(&repo, &["commit", "--no-gpg-sign", "-q", "-m", "initial"]);
|
|
203
|
+
|
|
204
|
+
let blame = blame_file(path.to_str().unwrap()).expect("git blame output");
|
|
205
|
+
let _ = fs::remove_dir_all(&repo);
|
|
206
|
+
|
|
207
|
+
assert_eq!(blame.len(), 2);
|
|
208
|
+
assert_eq!(blame["1"].author, "Jscpd Rs");
|
|
209
|
+
assert_eq!(blame["2"].line, "2");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
fn fragment(source_id: &str, start_line: usize, end_line: usize) -> Fragment {
|
|
213
|
+
Fragment {
|
|
214
|
+
source_id: source_id.to_string(),
|
|
215
|
+
start: location(start_line),
|
|
216
|
+
end: location(end_line),
|
|
217
|
+
range: [0, 0],
|
|
218
|
+
blame: None,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn location(line: usize) -> Location {
|
|
223
|
+
Location {
|
|
224
|
+
line,
|
|
225
|
+
column: 1,
|
|
226
|
+
position: 0,
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
fn git(repo: &Path, args: &[&str]) {
|
|
231
|
+
let status = Command::new("git")
|
|
232
|
+
.args(args)
|
|
233
|
+
.current_dir(repo)
|
|
234
|
+
.status()
|
|
235
|
+
.expect("run git");
|
|
236
|
+
assert!(status.success(), "git {args:?} failed");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
fn unique_temp_dir(label: &str) -> PathBuf {
|
|
240
|
+
let suffix = SystemTime::now()
|
|
241
|
+
.duration_since(UNIX_EPOCH)
|
|
242
|
+
.unwrap()
|
|
243
|
+
.as_nanos();
|
|
244
|
+
std::env::temp_dir().join(format!("jscpd-rs-{label}-{}-{suffix}", std::process::id()))
|
|
245
|
+
}
|
|
130
246
|
}
|