jscpd-rs 0.1.0 → 0.1.2
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 +26 -2
- package/Cargo.lock +1 -1
- package/Cargo.toml +3 -3
- package/README.md +33 -26
- package/docs/migrating-from-jscpd.md +15 -10
- package/docs/npm-release.md +52 -22
- package/docs/prebuilt-binaries.md +89 -0
- package/docs/release-checklist.md +46 -0
- package/docs/release-readiness.md +2 -2
- package/docs/user-guide.md +15 -7
- package/npm/lib/platform.js +92 -0
- package/npm/lib/run-binary.js +11 -12
- package/npm/prebuilt-targets.json +44 -0
- package/npm/scripts/postinstall.js +14 -9
- package/package.json +13 -2
- package/src/lib.rs +16 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 - 2026-06-01
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Rename the Windows prebuilt npm package to `jscpd-rs-win` to avoid npm
|
|
8
|
+
registry spam-policy false positives on the previous machine-generated name.
|
|
9
|
+
- Move the Linux arm64 prebuilt build to the Ubuntu 22.04 ARM runner for a more
|
|
10
|
+
stable native ARM publication path and older glibc baseline.
|
|
11
|
+
- Allow npm release workflow reruns for a single prebuilt target without
|
|
12
|
+
republishing the already-published main package.
|
|
13
|
+
|
|
14
|
+
## 0.1.1 - 2026-06-01
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- npm prebuilt binary distribution wiring for Linux x64 GNU, Linux arm64 GNU,
|
|
19
|
+
macOS x64, macOS arm64, and Windows x64 MSVC platform packages.
|
|
20
|
+
- Runtime npm shim resolution that prefers installed prebuilt optional
|
|
21
|
+
packages and falls back to the existing Cargo source-build path.
|
|
22
|
+
- npm package checks for platform package metadata, source-build fallback, and
|
|
23
|
+
local prebuilt-package smoke tests.
|
|
24
|
+
- GitHub Release npm publishing workflow that builds platform packages before
|
|
25
|
+
publishing the main `jscpd-rs` package.
|
|
26
|
+
|
|
3
27
|
## 0.1.0 - 2026-05-31
|
|
4
28
|
|
|
5
29
|
First release candidate for `jscpd-rs`, a native Rust clone of upstream
|
|
@@ -62,8 +86,8 @@ Recorded release-candidate public benchmark measurements from
|
|
|
62
86
|
- HTML output is self-contained and practically compatible, not pixel-perfect.
|
|
63
87
|
- The Rust crate exposes a native Rust API, not the upstream JavaScript package
|
|
64
88
|
API.
|
|
65
|
-
- The npm package
|
|
66
|
-
|
|
89
|
+
- The `jscpd-rs@0.1.0` npm package builds native binaries from source during
|
|
90
|
+
install.
|
|
67
91
|
- Full Prism grammar parity for every long-tail format is not attempted in this
|
|
68
92
|
release. Formats should be promoted from generic tokenization when concrete
|
|
69
93
|
coverage gates show missed upstream lines.
|
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "jscpd-rs"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.93"
|
|
6
6
|
license = "MIT"
|
|
7
|
-
description = "
|
|
7
|
+
description = "50x+ faster duplicate-code detector for CI/CD; jscpd-compatible CLI, SARIF, JSON, HTML reports"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
repository = "https://github.com/vv-bogdanov/jscpd-rs"
|
|
10
10
|
documentation = "https://docs.rs/jscpd-rs"
|
|
11
|
-
keywords = ["jscpd", "
|
|
11
|
+
keywords = ["jscpd", "duplicate-code", "clone-detection", "sarif", "ci"]
|
|
12
12
|
categories = ["command-line-utilities", "development-tools"]
|
|
13
13
|
include = [
|
|
14
14
|
"/CHANGELOG.md",
|
package/README.md
CHANGED
|
@@ -7,22 +7,24 @@
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.rust-lang.org/)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
crosses a configured threshold.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
50x+ faster duplicate-code detector for local development, CI/CD, and code
|
|
11
|
+
quality gates.
|
|
12
|
+
`jscpd-rs` scans a codebase, finds copy-paste fragments across files, writes
|
|
13
|
+
console, JSON, SARIF, HTML, XML, CSV, Markdown, badge, and Xcode reports, and
|
|
14
|
+
can fail a build when duplication crosses a configured threshold.
|
|
15
|
+
|
|
16
|
+
It is a native Rust implementation of the common
|
|
17
|
+
[`jscpd`](https://github.com/kucherenko/jscpd) command-line workflow:
|
|
18
|
+
upstream-style CLI flags, `.jscpd.json` and `package.json#jscpd`
|
|
19
|
+
configuration, report formats, exit-code behavior, Git blame, and server
|
|
20
|
+
snippet checks. The practical goal is simple: keep copy-paste detection
|
|
21
|
+
always-on without spending unnecessary CI minutes, developer waiting time,
|
|
22
|
+
cloud compute budget, or electricity on repeated quality gates.
|
|
23
|
+
|
|
24
|
+
Recorded public benchmark baselines show 50x+ speedups over upstream on the
|
|
25
|
+
covered repositories. The compatibility gate is coverage-first: on the same
|
|
26
|
+
inputs and options, `jscpd-rs` must not miss duplicated source lines reported
|
|
27
|
+
by upstream `jscpd`.
|
|
26
28
|
|
|
27
29
|
## Install
|
|
28
30
|
|
|
@@ -43,10 +45,11 @@ npx jscpd-rs --version
|
|
|
43
45
|
npx jscpd-rs .
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Current npm packaging note: `jscpd-rs` installs prebuilt Linux, macOS, and
|
|
49
|
+
Windows binaries where available, then falls back to building from source with
|
|
50
|
+
Cargo for unsupported platforms. The original `0.1.0` npm package was
|
|
51
|
+
source-build only; use `0.1.2+` for the full prebuilt-first path. See
|
|
52
|
+
[docs/prebuilt-binaries.md](docs/prebuilt-binaries.md).
|
|
50
53
|
|
|
51
54
|
From this repository:
|
|
52
55
|
|
|
@@ -114,7 +117,7 @@ If you already use upstream `jscpd`, see
|
|
|
114
117
|
|
|
115
118
|
## GitHub Actions
|
|
116
119
|
|
|
117
|
-
Install from crates.io
|
|
120
|
+
Install from crates.io:
|
|
118
121
|
|
|
119
122
|
```yaml
|
|
120
123
|
jobs:
|
|
@@ -169,6 +172,10 @@ npx skills add vv-bogdanov/jscpd-rs --skill dry-refactoring
|
|
|
169
172
|
|
|
170
173
|
- **Fast CI/CD gates:** duplicate detection should be cheap enough to run on
|
|
171
174
|
every pull request.
|
|
175
|
+
- **Low-friction rollout:** npm installs use prebuilt binaries on supported
|
|
176
|
+
Linux, macOS, and Windows targets, with a Cargo fallback for other platforms.
|
|
177
|
+
- **Actionable reports:** console, JSON, SARIF, HTML, XML, CSV, Markdown,
|
|
178
|
+
badge, Xcode, threshold, and AI-oriented reports are implemented natively.
|
|
172
179
|
- **Lower operating cost:** shorter scans reduce paid compute minutes and
|
|
173
180
|
repeated developer wait time.
|
|
174
181
|
- **Native detector path:** the detector does not embed or spawn JavaScript for
|
|
@@ -181,8 +188,8 @@ npx skills add vv-bogdanov/jscpd-rs --skill dry-refactoring
|
|
|
181
188
|
|
|
182
189
|
## What Works Today
|
|
183
190
|
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
The current release is a coverage-first compatible CLI replacement for common
|
|
192
|
+
`jscpd` duplicate-code and copy-paste detection workflows:
|
|
186
193
|
|
|
187
194
|
- `jscpd` and `jscpd-server` binaries with upstream-compatible command names;
|
|
188
195
|
- CLI and config option surface covered by compatibility scripts;
|
|
@@ -217,7 +224,7 @@ compare their reports.
|
|
|
217
224
|
|
|
218
225
|
## Performance
|
|
219
226
|
|
|
220
|
-
Latest recorded public benchmark baseline:
|
|
227
|
+
Latest recorded public benchmark baseline for duplicate-code detection:
|
|
221
228
|
|
|
222
229
|
| Repo | Format | Rust avg | Upstream avg | Speedup |
|
|
223
230
|
| --- | --- | ---: | ---: | ---: |
|
|
@@ -231,8 +238,8 @@ Reproduce the public benchmark and coverage suite:
|
|
|
231
238
|
PUBLIC=1 PUBLIC_RUNS=3 scripts/release-gate.sh
|
|
232
239
|
```
|
|
233
240
|
|
|
234
|
-
|
|
235
|
-
README numbers stay tied to a concrete commit and gate output.
|
|
241
|
+
Release-candidate workflows rerun the public suite before each new publication
|
|
242
|
+
so README numbers stay tied to a concrete commit and gate output.
|
|
236
243
|
|
|
237
244
|
## Library API
|
|
238
245
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Migrating From jscpd
|
|
2
2
|
|
|
3
|
-
`jscpd-rs` is a
|
|
4
|
-
workflow. It scans source trees,
|
|
5
|
-
|
|
3
|
+
`jscpd-rs` is a 50x+ faster duplicate-code detector and native Rust
|
|
4
|
+
implementation of the common upstream `jscpd` workflow. It scans source trees,
|
|
5
|
+
reports copy-paste fragments across files, writes console, JSON, SARIF, HTML,
|
|
6
|
+
XML, CSV, Markdown, badge, and Xcode reports, and can fail CI when duplication
|
|
7
|
+
crosses a configured threshold.
|
|
6
8
|
|
|
7
9
|
The migration goal for the first release is practical CLI/reporting
|
|
8
10
|
compatibility for CI and local scans, not exact JavaScript package API parity.
|
|
@@ -16,7 +18,7 @@ cargo install jscpd-rs --locked
|
|
|
16
18
|
jscpd --threshold 5 --exitCode 1 src
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
npm/npx
|
|
21
|
+
npm/npx:
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
24
|
npx jscpd-rs --threshold 5 --exitCode 1 src
|
|
@@ -125,8 +127,10 @@ These are intentional first-release limits:
|
|
|
125
127
|
tokenizer is needed;
|
|
126
128
|
- the Rust crate exposes a native Rust API, not the upstream JavaScript package
|
|
127
129
|
API;
|
|
128
|
-
-
|
|
129
|
-
|
|
130
|
+
- `jscpd-rs@0.1.2+` npm packaging uses prebuilt binaries on supported Linux,
|
|
131
|
+
macOS, and Windows targets, with a Cargo source-build fallback for
|
|
132
|
+
unsupported platforms. The original `0.1.0` npm package was source-build
|
|
133
|
+
only.
|
|
130
134
|
|
|
131
135
|
## Compare On Your Repository
|
|
132
136
|
|
|
@@ -178,7 +182,7 @@ Cargo-based `jscpd-rs` CI:
|
|
|
178
182
|
- run: jscpd src --reporters console,json --threshold 5 --exitCode 1
|
|
179
183
|
```
|
|
180
184
|
|
|
181
|
-
npm/npx-based `jscpd-rs` CI
|
|
185
|
+
npm/npx-based `jscpd-rs` CI:
|
|
182
186
|
|
|
183
187
|
```yaml
|
|
184
188
|
- uses: dtolnay/rust-toolchain@stable
|
|
@@ -188,6 +192,7 @@ npm/npx-based `jscpd-rs` CI after npm publication:
|
|
|
188
192
|
- run: npx jscpd-rs src --reporters console,json --threshold 5 --exitCode 1
|
|
189
193
|
```
|
|
190
194
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
`jscpd-rs@0.1.2+` npm installs use prebuilt binaries where available and fall
|
|
196
|
+
back to Cargo source-build on unsupported platforms. The original `0.1.0` npm
|
|
197
|
+
package still needs Rust available during installation; see the
|
|
198
|
+
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
package/docs/npm-release.md
CHANGED
|
@@ -13,18 +13,17 @@ Current npm readiness snapshot:
|
|
|
13
13
|
- Packed artifact audit: `jscpd-rs-0.1.0.tgz`, 96 files, about 169 KiB packed,
|
|
14
14
|
about 708 KiB unpacked.
|
|
15
15
|
|
|
16
|
-
The
|
|
16
|
+
The npm package is `jscpd-rs`. It exposes these bin commands:
|
|
17
17
|
|
|
18
18
|
- `jscpd-rs`: primary `npx jscpd-rs` entrypoint, runs the native `jscpd` CLI.
|
|
19
19
|
- `jscpd`: installed alias for the native `jscpd` CLI.
|
|
20
20
|
- `jscpd-server`: installed alias for the native server binary.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
changing the CLI behavior.
|
|
22
|
+
`jscpd-rs@0.1.2+` publishes prebuilt platform packages before the main
|
|
23
|
+
`jscpd-rs` package. The CLI behavior stays the same, and the source-build path
|
|
24
|
+
remains the fallback for unsupported platforms. The original `0.1.0` package
|
|
25
|
+
was source-build only; see the
|
|
26
|
+
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
|
28
27
|
|
|
29
28
|
Local verification:
|
|
30
29
|
|
|
@@ -36,14 +35,20 @@ That script verifies:
|
|
|
36
35
|
|
|
37
36
|
- `package.json` version matches `Cargo.toml`;
|
|
38
37
|
- `npm pack` includes the expected Rust source and npm shim files;
|
|
38
|
+
- `optionalDependencies` match `npm/prebuilt-targets.json` and the current
|
|
39
|
+
package version;
|
|
39
40
|
- `npm pack` includes the advertised `skills/` files used by the terminal tip;
|
|
40
41
|
- forbidden paths such as `jscpd/`, `target/`, `report/`, `scripts/`, and
|
|
41
42
|
`node_modules/` are not packed;
|
|
42
43
|
- `npm publish --dry-run --json` succeeds without publishing;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
44
|
+
if the current version is already published, this dry-run is skipped with an
|
|
45
|
+
explicit message;
|
|
46
|
+
- installing the packed tarball without Cargo and without optional prebuilt
|
|
47
|
+
packages fails with the expected Rust toolchain hint;
|
|
48
|
+
- source-build fallback exposes working `jscpd-rs`, `jscpd`, and
|
|
49
|
+
`jscpd-server` bin commands;
|
|
50
|
+
- a locally generated platform package exposes the same bin commands without
|
|
51
|
+
Cargo;
|
|
47
52
|
- `npx --package <local-tarball> jscpd-rs --version` works.
|
|
48
53
|
|
|
49
54
|
Before actual publication, run:
|
|
@@ -55,9 +60,10 @@ scripts/npm-package-check.sh
|
|
|
55
60
|
npm view jscpd-rs version
|
|
56
61
|
```
|
|
57
62
|
|
|
58
|
-
`npm view
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
For a new version, `npm view jscpd-rs@X.Y.Z version` should fail before the
|
|
64
|
+
release and return that version after publication. Do not run `npm publish`
|
|
65
|
+
manually unless the GitHub Release workflow fails and explicit fallback
|
|
66
|
+
approval is given.
|
|
61
67
|
|
|
62
68
|
Do not use `scripts/prepublish-check.sh` as the npm-only gate after the Cargo
|
|
63
69
|
crate and GitHub tag have already been published: that script is the full
|
|
@@ -84,15 +90,25 @@ specific GitHub Actions workflow through OIDC, so the workflow can publish
|
|
|
84
90
|
without a long-lived npm token. For public packages published from public
|
|
85
91
|
repositories, npm also generates provenance attestations automatically.
|
|
86
92
|
|
|
87
|
-
This repository provides a
|
|
93
|
+
This repository provides a publish workflow:
|
|
88
94
|
|
|
89
95
|
```text
|
|
90
96
|
.github/workflows/npm-publish.yml
|
|
91
97
|
```
|
|
92
98
|
|
|
93
|
-
|
|
99
|
+
The workflow runs automatically when a non-draft, non-prerelease GitHub Release
|
|
100
|
+
is published. It checks out the release tag, verifies that the tag matches the
|
|
101
|
+
`package.json` version, verifies that the main npm version is not already
|
|
102
|
+
published, builds platform packages in a native runner matrix, publishes those
|
|
103
|
+
platform packages first, runs `scripts/npm-package-check.sh`, and then
|
|
104
|
+
publishes the main `jscpd-rs` package.
|
|
94
105
|
|
|
95
|
-
|
|
106
|
+
It can also be run manually from GitHub Actions as a fallback by entering
|
|
107
|
+
`jscpd-rs` in the confirmation input.
|
|
108
|
+
|
|
109
|
+
Configure npm for every package listed in `docs/prebuilt-binaries.md`:
|
|
110
|
+
|
|
111
|
+
1. Open the package settings on npmjs.com.
|
|
96
112
|
2. Go to **Trusted Publisher**.
|
|
97
113
|
3. Select **GitHub Actions**.
|
|
98
114
|
4. Use these values:
|
|
@@ -102,7 +118,16 @@ Configure npm:
|
|
|
102
118
|
- Environment name: leave empty
|
|
103
119
|
- Allowed actions: `npm publish`
|
|
104
120
|
|
|
105
|
-
Then publish from GitHub:
|
|
121
|
+
Then publish automatically from GitHub:
|
|
122
|
+
|
|
123
|
+
1. Update `Cargo.toml` and `package.json` to the same version.
|
|
124
|
+
2. Update `package.json#optionalDependencies` to the same version for every
|
|
125
|
+
platform package.
|
|
126
|
+
3. Run the release gates.
|
|
127
|
+
4. Create and publish a GitHub Release with tag `vX.Y.Z`.
|
|
128
|
+
5. GitHub Actions will run `npm-publish` from that release tag.
|
|
129
|
+
|
|
130
|
+
Manual fallback:
|
|
106
131
|
|
|
107
132
|
1. Open GitHub Actions.
|
|
108
133
|
2. Select the `npm-publish` workflow.
|
|
@@ -110,7 +135,12 @@ Then publish from GitHub:
|
|
|
110
135
|
4. Enter `jscpd-rs` for `package_name`.
|
|
111
136
|
5. Run the workflow.
|
|
112
137
|
|
|
113
|
-
If npm does not allow Trusted Publishing configuration before
|
|
114
|
-
version exists,
|
|
115
|
-
|
|
116
|
-
|
|
138
|
+
If npm does not allow Trusted Publishing configuration before a platform package
|
|
139
|
+
version exists, add a short-lived `NPM_TOKEN` GitHub secret for the first
|
|
140
|
+
platform-package bootstrap, then immediately configure Trusted Publishing and
|
|
141
|
+
revoke the token.
|
|
142
|
+
|
|
143
|
+
After Trusted Publishing is verified, revoke temporary npm tokens and consider
|
|
144
|
+
setting the npm package publishing access to disallow traditional tokens. The
|
|
145
|
+
full GitHub Release publishing flow, including crates.io publishing, is tracked
|
|
146
|
+
in `docs/release-checklist.md`.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Prebuilt Binary Distribution
|
|
2
|
+
|
|
3
|
+
`jscpd-rs` is a native Rust CLI. Starting with `jscpd-rs@0.1.2`, npm
|
|
4
|
+
distribution uses a small main package plus platform-specific optional
|
|
5
|
+
packages. The original `0.1.0` npm package was source-build only.
|
|
6
|
+
|
|
7
|
+
The main `jscpd-rs` package keeps the public bin names: `jscpd-rs`, `jscpd`,
|
|
8
|
+
and `jscpd-server`.
|
|
9
|
+
|
|
10
|
+
## Runtime Behavior
|
|
11
|
+
|
|
12
|
+
- The JS shim first looks for a matching prebuilt optional package.
|
|
13
|
+
- If a prebuilt package is installed, the shim runs its native binary directly.
|
|
14
|
+
- If no prebuilt package exists for the platform, the existing source-build
|
|
15
|
+
path remains the fallback.
|
|
16
|
+
- `JSCPD_RS_FORCE_BUILD=1` forces the fallback build path.
|
|
17
|
+
- `JSCPD_RS_SKIP_POSTINSTALL=1` skips install-time builds for package tests and
|
|
18
|
+
advanced users who provide binaries another way.
|
|
19
|
+
|
|
20
|
+
Avoid default postinstall downloads from GitHub Releases. Optional platform
|
|
21
|
+
packages are more reproducible, work better with npm mirrors and lockfiles, and
|
|
22
|
+
avoid surprising network access during install.
|
|
23
|
+
|
|
24
|
+
## Platform Packages
|
|
25
|
+
|
|
26
|
+
The target matrix is defined in `npm/prebuilt-targets.json`.
|
|
27
|
+
|
|
28
|
+
| Package | Rust target | Runner |
|
|
29
|
+
| --- | --- | --- |
|
|
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-22.04-arm` |
|
|
32
|
+
| `jscpd-rs-darwin-x64` | `x86_64-apple-darwin` | `macos-15-intel` |
|
|
33
|
+
| `jscpd-rs-darwin-arm64` | `aarch64-apple-darwin` | `macos-15` |
|
|
34
|
+
| `jscpd-rs-win` | `x86_64-pc-windows-msvc` | `windows-2025` |
|
|
35
|
+
|
|
36
|
+
Consider Linux musl and Windows arm64 only after install data or user reports
|
|
37
|
+
show demand.
|
|
38
|
+
|
|
39
|
+
## Package Checks
|
|
40
|
+
|
|
41
|
+
`scripts/npm-package-check.sh` verifies:
|
|
42
|
+
|
|
43
|
+
- the root npm version matches `Cargo.toml`;
|
|
44
|
+
- `optionalDependencies` exactly match `npm/prebuilt-targets.json`;
|
|
45
|
+
- every optional platform dependency uses the same version as the main package;
|
|
46
|
+
- the main npm package contains the runtime shim and target metadata;
|
|
47
|
+
- no Cargo/no prebuilt install fails with the expected Rust toolchain hint;
|
|
48
|
+
- source-build fallback works with optional dependencies omitted;
|
|
49
|
+
- a locally generated platform package works without Cargo;
|
|
50
|
+
- `npx --package <local-tarball> jscpd-rs --version` works.
|
|
51
|
+
|
|
52
|
+
## Release Workflow
|
|
53
|
+
|
|
54
|
+
The GitHub Release workflow in `.github/workflows/npm-publish.yml` publishes in
|
|
55
|
+
this order:
|
|
56
|
+
|
|
57
|
+
1. Verify that the GitHub Release tag matches `package.json`.
|
|
58
|
+
2. Build platform packages in a native runner matrix.
|
|
59
|
+
3. Smoke-test `jscpd --version` and `jscpd-server --version` for each package.
|
|
60
|
+
4. Publish the platform packages.
|
|
61
|
+
5. Run `scripts/npm-package-check.sh`.
|
|
62
|
+
6. Publish the main `jscpd-rs` package.
|
|
63
|
+
|
|
64
|
+
The workflow publishes with npm provenance enabled. It is designed for Trusted
|
|
65
|
+
Publishing, but `NPM_TOKEN` can be kept as a temporary bootstrap fallback for
|
|
66
|
+
new platform packages if npm does not allow Trusted Publisher setup before the
|
|
67
|
+
first version exists.
|
|
68
|
+
|
|
69
|
+
## npm Setup
|
|
70
|
+
|
|
71
|
+
Configure Trusted Publishing for each npm package with:
|
|
72
|
+
|
|
73
|
+
- Organization or user: `vv-bogdanov`
|
|
74
|
+
- Repository: `jscpd-rs`
|
|
75
|
+
- Workflow filename: `npm-publish.yml`
|
|
76
|
+
- Environment name: leave empty
|
|
77
|
+
- Allowed actions: `npm publish`
|
|
78
|
+
|
|
79
|
+
Packages:
|
|
80
|
+
|
|
81
|
+
- `jscpd-rs`
|
|
82
|
+
- `jscpd-rs-linux-x64-gnu`
|
|
83
|
+
- `jscpd-rs-linux-arm64-gnu`
|
|
84
|
+
- `jscpd-rs-darwin-x64`
|
|
85
|
+
- `jscpd-rs-darwin-arm64`
|
|
86
|
+
- `jscpd-rs-win`
|
|
87
|
+
|
|
88
|
+
If a temporary npm token is used for the first platform-package bootstrap,
|
|
89
|
+
revoke it after Trusted Publishing succeeds.
|
|
@@ -178,12 +178,58 @@ two explicit commands above are a manual smoke equivalent for post-tag checks.
|
|
|
178
178
|
Track these after the first release candidate:
|
|
179
179
|
|
|
180
180
|
- Reduce noisy extra Rust findings where they are user-visible false positives.
|
|
181
|
+
- Verify the first npm prebuilt platform-package publication before broad npm
|
|
182
|
+
promotion so Node users can install without Cargo; see the
|
|
183
|
+
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
|
181
184
|
- Add native persistent store/cache only if release-scale benchmark data needs
|
|
182
185
|
it.
|
|
183
186
|
- Tighten MCP Streamable HTTP SDK edge cases if real MCP clients require them.
|
|
184
187
|
- Promote long-tail tokenizers only from concrete missed-coverage evidence.
|
|
185
188
|
- File upstream bug reports from `docs/upstream-issue-drafts.md`.
|
|
186
189
|
|
|
190
|
+
## Automated GitHub Release Publishing
|
|
191
|
+
|
|
192
|
+
After the first manual Cargo/npm bootstrap, future releases should use GitHub
|
|
193
|
+
Release publication as the single publish trigger.
|
|
194
|
+
|
|
195
|
+
Configured workflows:
|
|
196
|
+
|
|
197
|
+
- `.github/workflows/crates-publish.yml`
|
|
198
|
+
- `.github/workflows/npm-publish.yml`
|
|
199
|
+
|
|
200
|
+
Both workflows run on `release.published` for non-draft, non-prerelease GitHub
|
|
201
|
+
Releases. Both check out the release tag and verify that `vX.Y.Z` matches the
|
|
202
|
+
package version before publishing. `crates-publish` publishes the Rust crate to
|
|
203
|
+
crates.io; docs.rs builds documentation automatically after crates.io accepts
|
|
204
|
+
the crate. `npm-publish` builds and publishes prebuilt npm platform packages
|
|
205
|
+
before publishing the main npm package.
|
|
206
|
+
|
|
207
|
+
Release flow for future versions:
|
|
208
|
+
|
|
209
|
+
1. Update `Cargo.toml` and `package.json` to the same version.
|
|
210
|
+
2. Update `package.json#optionalDependencies` to the same version for every
|
|
211
|
+
platform package.
|
|
212
|
+
3. Update `CHANGELOG.md`, README benchmark numbers, and release docs.
|
|
213
|
+
4. Run the release gates, including `scripts/package-check.sh`.
|
|
214
|
+
5. Commit and push `main`.
|
|
215
|
+
6. Create and publish a GitHub Release with tag `vX.Y.Z`.
|
|
216
|
+
7. Confirm `crates-publish` completed successfully.
|
|
217
|
+
8. Confirm `npm-publish` built and published all platform packages before the
|
|
218
|
+
main `jscpd-rs` package.
|
|
219
|
+
9. Check crates.io, docs.rs, and npm package pages.
|
|
220
|
+
|
|
221
|
+
Trusted Publishing setup:
|
|
222
|
+
|
|
223
|
+
- crates.io: configure a trusted publisher for repository
|
|
224
|
+
`vv-bogdanov/jscpd-rs` and workflow `crates-publish.yml`.
|
|
225
|
+
- npm: configure a trusted publisher for repository `vv-bogdanov/jscpd-rs` and
|
|
226
|
+
workflow `npm-publish.yml` on `jscpd-rs` and every platform package listed in
|
|
227
|
+
`docs/prebuilt-binaries.md`.
|
|
228
|
+
|
|
229
|
+
Use no GitHub environment name unless the workflow is updated to declare one.
|
|
230
|
+
After Trusted Publishing works, revoke temporary npm/crates tokens and keep
|
|
231
|
+
token-based publishing as an emergency fallback only.
|
|
232
|
+
|
|
187
233
|
## CI Speed Plan
|
|
188
234
|
|
|
189
235
|
Keep this as part of the release plan:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Release Readiness
|
|
2
2
|
|
|
3
|
-
Last updated: 2026-
|
|
3
|
+
Last updated: 2026-06-01.
|
|
4
4
|
|
|
5
5
|
This is the working component checklist for the first release. The authoritative
|
|
6
6
|
policy decisions are still in `docs/release-decisions.md`; this file tracks the
|
|
@@ -36,7 +36,7 @@ current implementation status.
|
|
|
36
36
|
| Terminal cosmetics | practical parity | Important messages are gated; exact wrapping/order remains lower priority. |
|
|
37
37
|
| 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
38
|
| Server snippet matching | optimized baseline | Native `/api/check` and MCP `check_duplication` are functional and reuse project token maps from the last scan; add a dedicated window index only if real server benchmarks require it. |
|
|
39
|
-
| Npm prebuilt binaries |
|
|
39
|
+
| Npm prebuilt binaries | ready for 0.1.2 | 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+`. See the [prebuilt binary distribution plan](prebuilt-binaries.md). |
|
|
40
40
|
| 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
41
|
|
|
42
42
|
## Post-MVP
|
package/docs/user-guide.md
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
# jscpd-rs User Guide
|
|
2
2
|
|
|
3
|
-
`jscpd-rs` is a
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`jscpd-rs` is a 50x+ faster duplicate-code detector for local development,
|
|
4
|
+
CI/CD, and code quality gates. It scans source trees, detects copy-paste
|
|
5
|
+
fragments across files, writes console, JSON, SARIF, HTML, XML, CSV, Markdown,
|
|
6
|
+
badge, and Xcode reports, fails CI on duplication thresholds, and serves
|
|
7
|
+
snippet checks over HTTP/MCP.
|
|
8
|
+
|
|
9
|
+
It implements the common upstream `jscpd` CLI workflow with native Rust
|
|
10
|
+
performance: upstream-style flags, `.jscpd.json` and `package.json#jscpd`
|
|
11
|
+
configuration, Git blame, report generation, exit-code behavior, and a native
|
|
12
|
+
server.
|
|
6
13
|
|
|
7
14
|
This guide is intentionally user-facing. Release policy, benchmark evidence,
|
|
8
15
|
and compatibility details live in the release docs linked from the README.
|
|
9
16
|
|
|
10
17
|
## Installation
|
|
11
18
|
|
|
12
|
-
Install the Rust binaries from crates.io
|
|
19
|
+
Install the Rust binaries from crates.io:
|
|
13
20
|
|
|
14
21
|
```bash
|
|
15
22
|
cargo install jscpd-rs --locked
|
|
@@ -22,9 +29,10 @@ npm install -g jscpd-rs
|
|
|
22
29
|
npx jscpd-rs --version
|
|
23
30
|
```
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
`jscpd-rs@0.1.2+` installs prebuilt Linux, macOS, and Windows binaries where
|
|
33
|
+
available and falls back to building from source with Cargo for unsupported
|
|
34
|
+
platforms. The original `0.1.0` npm package was source-build only. See the
|
|
35
|
+
[prebuilt binary distribution plan](prebuilt-binaries.md).
|
|
28
36
|
|
|
29
37
|
Install from a checkout:
|
|
30
38
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const targets = require("../prebuilt-targets.json");
|
|
7
|
+
|
|
8
|
+
function packageRoot() {
|
|
9
|
+
return path.resolve(__dirname, "..", "..");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function exeName(name, platform = process.platform) {
|
|
13
|
+
return platform === "win32" ? `${name}.exe` : name;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function sourceBinaryPath(name) {
|
|
17
|
+
const targetDir =
|
|
18
|
+
process.env.CARGO_TARGET_DIR || path.join(packageRoot(), "target");
|
|
19
|
+
return path.join(targetDir, "release", exeName(name));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function detectLinuxLibc() {
|
|
23
|
+
if (process.platform !== "linux") {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const report =
|
|
28
|
+
process.report && typeof process.report.getReport === "function"
|
|
29
|
+
? process.report.getReport()
|
|
30
|
+
: undefined;
|
|
31
|
+
if (report && report.header && report.header.glibcVersionRuntime) {
|
|
32
|
+
return "glibc";
|
|
33
|
+
}
|
|
34
|
+
return "musl";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function currentTargetKey() {
|
|
38
|
+
const libc = detectLinuxLibc();
|
|
39
|
+
for (const [key, target] of Object.entries(targets)) {
|
|
40
|
+
if (target.os !== process.platform || target.cpu !== process.arch) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (target.os === "linux" && target.libc !== libc) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function currentTarget() {
|
|
52
|
+
const key = currentTargetKey();
|
|
53
|
+
return key ? { key, ...targets[key] } : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolvePrebuiltBinary(name) {
|
|
57
|
+
if (process.env.JSCPD_RS_FORCE_BUILD === "1") {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const target = currentTarget();
|
|
62
|
+
if (!target) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let packageJson;
|
|
67
|
+
try {
|
|
68
|
+
packageJson = require.resolve(`${target.packageName}/package.json`, {
|
|
69
|
+
paths: [packageRoot()],
|
|
70
|
+
});
|
|
71
|
+
} catch {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const binary = path.join(path.dirname(packageJson), "bin", exeName(name, target.os));
|
|
76
|
+
return fs.existsSync(binary) ? binary : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveBinary(name) {
|
|
80
|
+
return resolvePrebuiltBinary(name) || sourceBinaryPath(name);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
currentTarget,
|
|
85
|
+
currentTargetKey,
|
|
86
|
+
exeName,
|
|
87
|
+
packageRoot,
|
|
88
|
+
resolveBinary,
|
|
89
|
+
resolvePrebuiltBinary,
|
|
90
|
+
sourceBinaryPath,
|
|
91
|
+
targets,
|
|
92
|
+
};
|
package/npm/lib/run-binary.js
CHANGED
|
@@ -3,20 +3,19 @@
|
|
|
3
3
|
const fs = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const { spawnSync } = require("node:child_process");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function binaryPath(name) {
|
|
12
|
-
const exe = process.platform === "win32" ? `${name}.exe` : name;
|
|
13
|
-
const targetDir =
|
|
14
|
-
process.env.CARGO_TARGET_DIR || path.join(packageRoot(), "target");
|
|
15
|
-
return path.join(targetDir, "release", exe);
|
|
16
|
-
}
|
|
6
|
+
const {
|
|
7
|
+
packageRoot,
|
|
8
|
+
resolvePrebuiltBinary,
|
|
9
|
+
sourceBinaryPath,
|
|
10
|
+
} = require("./platform");
|
|
17
11
|
|
|
18
12
|
function buildIfMissing(name) {
|
|
19
|
-
const
|
|
13
|
+
const prebuilt = resolvePrebuiltBinary(name);
|
|
14
|
+
if (prebuilt) {
|
|
15
|
+
return prebuilt;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const binary = sourceBinaryPath(name);
|
|
20
19
|
if (fs.existsSync(binary)) {
|
|
21
20
|
return binary;
|
|
22
21
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"linux-x64-gnu": {
|
|
3
|
+
"packageName": "jscpd-rs-linux-x64-gnu",
|
|
4
|
+
"description": "Prebuilt Linux x64 GNU binaries for jscpd-rs",
|
|
5
|
+
"os": "linux",
|
|
6
|
+
"cpu": "x64",
|
|
7
|
+
"libc": "glibc",
|
|
8
|
+
"rustTarget": "x86_64-unknown-linux-gnu",
|
|
9
|
+
"runner": "ubuntu-24.04"
|
|
10
|
+
},
|
|
11
|
+
"linux-arm64-gnu": {
|
|
12
|
+
"packageName": "jscpd-rs-linux-arm64-gnu",
|
|
13
|
+
"description": "Prebuilt Linux arm64 GNU binaries for jscpd-rs",
|
|
14
|
+
"os": "linux",
|
|
15
|
+
"cpu": "arm64",
|
|
16
|
+
"libc": "glibc",
|
|
17
|
+
"rustTarget": "aarch64-unknown-linux-gnu",
|
|
18
|
+
"runner": "ubuntu-22.04-arm"
|
|
19
|
+
},
|
|
20
|
+
"darwin-x64": {
|
|
21
|
+
"packageName": "jscpd-rs-darwin-x64",
|
|
22
|
+
"description": "Prebuilt macOS x64 binaries for jscpd-rs",
|
|
23
|
+
"os": "darwin",
|
|
24
|
+
"cpu": "x64",
|
|
25
|
+
"rustTarget": "x86_64-apple-darwin",
|
|
26
|
+
"runner": "macos-15-intel"
|
|
27
|
+
},
|
|
28
|
+
"darwin-arm64": {
|
|
29
|
+
"packageName": "jscpd-rs-darwin-arm64",
|
|
30
|
+
"description": "Prebuilt macOS arm64 binaries for jscpd-rs",
|
|
31
|
+
"os": "darwin",
|
|
32
|
+
"cpu": "arm64",
|
|
33
|
+
"rustTarget": "aarch64-apple-darwin",
|
|
34
|
+
"runner": "macos-15"
|
|
35
|
+
},
|
|
36
|
+
"win32-x64-msvc": {
|
|
37
|
+
"packageName": "jscpd-rs-win",
|
|
38
|
+
"description": "Prebuilt Windows x64 binaries for jscpd-rs",
|
|
39
|
+
"os": "win32",
|
|
40
|
+
"cpu": "x64",
|
|
41
|
+
"rustTarget": "x86_64-pc-windows-msvc",
|
|
42
|
+
"runner": "windows-2025"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const fs = require("node:fs");
|
|
4
|
-
const path = require("node:path");
|
|
5
4
|
const { spawnSync } = require("node:child_process");
|
|
5
|
+
const {
|
|
6
|
+
packageRoot,
|
|
7
|
+
resolvePrebuiltBinary,
|
|
8
|
+
sourceBinaryPath,
|
|
9
|
+
} = require("../lib/platform");
|
|
6
10
|
|
|
7
|
-
const root =
|
|
11
|
+
const root = packageRoot();
|
|
8
12
|
const cargo = process.env.CARGO || "cargo";
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const binaries = ["jscpd", "jscpd-server"].map((name) =>
|
|
13
|
-
path.join(releaseDir, `${name}${exeSuffix}`),
|
|
14
|
-
);
|
|
13
|
+
const binaryNames = ["jscpd", "jscpd-server"];
|
|
14
|
+
const prebuiltBinaries = binaryNames.map(resolvePrebuiltBinary);
|
|
15
|
+
const sourceBinaries = binaryNames.map(sourceBinaryPath);
|
|
15
16
|
|
|
16
17
|
if (process.env.JSCPD_RS_SKIP_POSTINSTALL === "1") {
|
|
17
18
|
console.log("jscpd-rs: skipping native build because JSCPD_RS_SKIP_POSTINSTALL=1");
|
|
18
19
|
process.exit(0);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
if (
|
|
22
|
+
if (prebuiltBinaries.every(Boolean)) {
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (sourceBinaries.every((binary) => fs.existsSync(binary))) {
|
|
22
27
|
process.exit(0);
|
|
23
28
|
}
|
|
24
29
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jscpd-rs",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "50x+ faster duplicate-code detector for CI/CD; jscpd-compatible CLI, SARIF, JSON, HTML reports",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -18,11 +18,22 @@
|
|
|
18
18
|
"duplication",
|
|
19
19
|
"duplicate-code",
|
|
20
20
|
"code-duplication",
|
|
21
|
+
"copy-paste-detection",
|
|
21
22
|
"clone-detection",
|
|
23
|
+
"code-quality",
|
|
22
24
|
"ci",
|
|
25
|
+
"cicd",
|
|
23
26
|
"sarif",
|
|
27
|
+
"code-scanning",
|
|
24
28
|
"cli"
|
|
25
29
|
],
|
|
30
|
+
"optionalDependencies": {
|
|
31
|
+
"jscpd-rs-darwin-arm64": "0.1.2",
|
|
32
|
+
"jscpd-rs-darwin-x64": "0.1.2",
|
|
33
|
+
"jscpd-rs-linux-arm64-gnu": "0.1.2",
|
|
34
|
+
"jscpd-rs-linux-x64-gnu": "0.1.2",
|
|
35
|
+
"jscpd-rs-win": "0.1.2"
|
|
36
|
+
},
|
|
26
37
|
"bin": {
|
|
27
38
|
"jscpd-rs": "npm/bin/jscpd-rs.js",
|
|
28
39
|
"jscpd": "npm/bin/jscpd-rs.js",
|
package/src/lib.rs
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
#![doc(html_root_url = "https://docs.rs/jscpd-rs/0.1.
|
|
1
|
+
#![doc(html_root_url = "https://docs.rs/jscpd-rs/0.1.2")]
|
|
2
2
|
|
|
3
|
-
//! Native Rust API for `jscpd-rs`, a
|
|
4
|
-
//!
|
|
3
|
+
//! Native Rust API for `jscpd-rs`, a 50x+ faster duplicate-code detector for
|
|
4
|
+
//! local development and CI/CD.
|
|
5
5
|
//!
|
|
6
|
-
//!
|
|
6
|
+
//! `jscpd-rs` scans a codebase, finds copy-paste fragments across files, writes
|
|
7
|
+
//! console, JSON, SARIF, HTML, XML, CSV, Markdown, badge, and Xcode reports,
|
|
8
|
+
//! and can fail a build when duplication crosses a configured threshold.
|
|
9
|
+
//!
|
|
10
|
+
//! It is a native Rust implementation of the common
|
|
11
|
+
//! [`jscpd`](https://github.com/kucherenko/jscpd) command-line workflow:
|
|
12
|
+
//! upstream-style CLI flags, `.jscpd.json` and `package.json#jscpd`
|
|
13
|
+
//! configuration, report formats, exit-code behavior, Git blame, and server
|
|
14
|
+
//! snippet checks. The current public benchmark suite records 50x+ speedups on
|
|
15
|
+
//! pinned React, Next.js, and Prometheus cases while using a coverage-first
|
|
16
|
+
//! compatibility gate against upstream `jscpd`.
|
|
17
|
+
//!
|
|
18
|
+
//! This crate exposes the same detector core used by the `jscpd` and
|
|
7
19
|
//! `jscpd-server` binaries: option parsing, file discovery, tokenization,
|
|
8
20
|
//! duplicate detection, statistics, and in-memory source checks.
|
|
9
21
|
//!
|