code-gauge 4.6.0 → 4.7.1

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.
Files changed (57) hide show
  1. package/README.md +25 -0
  2. package/THIRD-PARTY-NOTICES.txt +8911 -0
  3. package/dist/cliConfig.cjs +1 -1
  4. package/dist/cliConfig.cjs.map +1 -1
  5. package/dist/cliConfig.js +1 -1
  6. package/dist/diffCommand.cjs +3 -3
  7. package/dist/diffCommand.cjs.map +1 -1
  8. package/dist/diffCommand.js +3 -3
  9. package/dist/diffCommand.js.map +1 -1
  10. package/dist/languages.cjs +1 -1
  11. package/dist/languages.cjs.map +1 -1
  12. package/dist/languages.js +1 -1
  13. package/dist/languages.js.map +1 -1
  14. package/dist/metrics.cjs +1 -1
  15. package/dist/metrics.cjs.map +1 -1
  16. package/dist/metrics.d.ts +8 -3
  17. package/dist/metrics.js +1 -1
  18. package/dist/metrics.js.map +1 -1
  19. package/dist/nativeMetrics.cjs +3 -3
  20. package/dist/nativeMetrics.cjs.map +1 -1
  21. package/dist/nativeMetrics.d.ts +30 -0
  22. package/dist/nativeMetrics.js +3 -3
  23. package/dist/nativeMetrics.js.map +1 -1
  24. package/dist/scan.cjs +1 -1
  25. package/dist/scan.cjs.map +1 -1
  26. package/dist/scan.d.ts +2 -2
  27. package/dist/scan.js +1 -1
  28. package/dist/scan.js.map +1 -1
  29. package/dist/wasmBinding.cjs +2 -0
  30. package/dist/wasmBinding.cjs.map +1 -0
  31. package/dist/wasmBinding.d.ts +9 -0
  32. package/dist/wasmBinding.js +2 -0
  33. package/dist/wasmBinding.js.map +1 -0
  34. package/dist/worker.cjs +2 -0
  35. package/dist/worker.cjs.map +1 -0
  36. package/dist/worker.d.ts +1 -0
  37. package/dist/worker.js +2 -0
  38. package/dist/worker.js.map +1 -0
  39. package/native/Cargo.lock +1 -0
  40. package/native/Cargo.toml +6 -2
  41. package/native/build.rs +4 -1
  42. package/native/code-gauge.wasm +0 -0
  43. package/native/src/complexity.rs +134 -230
  44. package/native/src/dep_degree.rs +42 -39
  45. package/native/src/duplication.rs +65 -63
  46. package/native/src/functions.rs +169 -152
  47. package/native/src/languages.rs +20 -0
  48. package/native/src/lib.rs +41 -42
  49. package/native/src/measure.rs +109 -123
  50. package/native/src/napi.rs +104 -0
  51. package/native/src/ncss.rs +79 -84
  52. package/native/src/near_miss.rs +7 -7
  53. package/native/src/tree_index.rs +110 -0
  54. package/native/src/util.rs +17 -12
  55. package/native/src/wasm.rs +128 -0
  56. package/native/src/worker_pool.rs +53 -0
  57. package/package.json +16 -11
@@ -0,0 +1,128 @@
1
+ //! The WebAssembly binding for runtimes without N-API, such as Cloudflare Workers
2
+ //! (src/wasmBinding.ts). Strings cross linear memory as UTF-8: the host copies each input into a
3
+ //! buffer from `alloc`, whose ownership passes to the called function, and reads the JSON result
4
+ //! (status 0) or error message (status 1) through `result_ptr`/`result_len` before the next call.
5
+
6
+ use std::cell::RefCell;
7
+
8
+ thread_local! {
9
+ static RESULT: RefCell<String> = const { RefCell::new(String::new()) };
10
+ }
11
+
12
+ #[no_mangle]
13
+ pub extern "C" fn payload_version() -> u32 {
14
+ crate::payload_version()
15
+ }
16
+
17
+ /// Allocates with an exact layout: `Vec::with_capacity` may over-allocate, so rebuilding a `Vec`
18
+ /// from the pointer and `len` would not be sound.
19
+ #[no_mangle]
20
+ pub extern "C" fn alloc(len: usize) -> *mut u8 {
21
+ if len == 0 {
22
+ return std::ptr::NonNull::dangling().as_ptr();
23
+ }
24
+ let pointer = unsafe { std::alloc::alloc(byte_layout(len)) };
25
+ if pointer.is_null() {
26
+ std::alloc::handle_alloc_error(byte_layout(len));
27
+ }
28
+ pointer
29
+ }
30
+
31
+ #[no_mangle]
32
+ pub extern "C" fn result_ptr() -> *const u8 {
33
+ RESULT.with_borrow(|result| result.as_ptr())
34
+ }
35
+
36
+ #[no_mangle]
37
+ pub extern "C" fn result_len() -> usize {
38
+ RESULT.with_borrow(|result| result.len())
39
+ }
40
+
41
+ /// Optional settings arrive as f64 (exact for every u32) with a negative value for "absent", so
42
+ /// the host needs no BigInt or presence flags.
43
+ #[no_mangle]
44
+ #[allow(clippy::too_many_arguments)]
45
+ pub unsafe extern "C" fn measure_code(
46
+ code_ptr: *mut u8,
47
+ code_len: usize,
48
+ language_ptr: *mut u8,
49
+ language_len: usize,
50
+ include_syntax_tree: u32,
51
+ min_tokens: f64,
52
+ max_gap_tokens: f64,
53
+ min_similarity_percent: f64,
54
+ include_cross_file_data: u32,
55
+ ) -> u32 {
56
+ // Both buffers are taken before `?` so that neither leaks when the other is invalid.
57
+ let code = take_string(code_ptr, code_len);
58
+ let language = take_string(language_ptr, language_len);
59
+ store_result((|| {
60
+ crate::measure_code(
61
+ &code?,
62
+ &language?,
63
+ include_syntax_tree != 0,
64
+ to_option(min_tokens),
65
+ to_option(max_gap_tokens),
66
+ to_option(min_similarity_percent),
67
+ include_cross_file_data != 0,
68
+ )
69
+ })())
70
+ }
71
+
72
+ #[no_mangle]
73
+ pub unsafe extern "C" fn collect_cross_file_data(
74
+ code_ptr: *mut u8,
75
+ code_len: usize,
76
+ language_ptr: *mut u8,
77
+ language_len: usize,
78
+ min_tokens: f64,
79
+ ) -> u32 {
80
+ let code = take_string(code_ptr, code_len);
81
+ let language = take_string(language_ptr, language_len);
82
+ store_result((|| {
83
+ crate::collect_cross_file_data(&code?, &language?, to_option(min_tokens))
84
+ })())
85
+ }
86
+
87
+ #[no_mangle]
88
+ pub unsafe extern "C" fn collect_function_token_sequences(
89
+ code_ptr: *mut u8,
90
+ code_len: usize,
91
+ language_ptr: *mut u8,
92
+ language_len: usize,
93
+ ) -> u32 {
94
+ let code = take_string(code_ptr, code_len);
95
+ let language = take_string(language_ptr, language_len);
96
+ store_result((|| {
97
+ crate::collect_function_token_sequences(&code?, &language?)
98
+ })())
99
+ }
100
+
101
+ /// Takes ownership of a buffer returned by `alloc(len)` and filled by the host.
102
+ unsafe fn take_string(ptr: *mut u8, len: usize) -> Result<String, String> {
103
+ if len == 0 {
104
+ return Ok(String::new());
105
+ }
106
+ let text = std::str::from_utf8(std::slice::from_raw_parts(ptr, len))
107
+ .map(str::to_owned)
108
+ .map_err(|error| error.to_string());
109
+ std::alloc::dealloc(ptr, byte_layout(len));
110
+ text
111
+ }
112
+
113
+ fn byte_layout(len: usize) -> std::alloc::Layout {
114
+ std::alloc::Layout::array::<u8>(len).expect("buffer size overflows isize")
115
+ }
116
+
117
+ fn to_option(value: f64) -> Option<u32> {
118
+ (value >= 0.0).then_some(value as u32)
119
+ }
120
+
121
+ fn store_result(result: Result<String, String>) -> u32 {
122
+ let (status, text) = match result {
123
+ Ok(json) => (0, json),
124
+ Err(message) => (1, message),
125
+ };
126
+ RESULT.set(text);
127
+ status
128
+ }
@@ -0,0 +1,53 @@
1
+ use std::num::NonZeroUsize;
2
+ use std::sync::{mpsc, Arc, Mutex, OnceLock};
3
+ use std::thread;
4
+
5
+ type Job = Box<dyn FnOnce() + Send>;
6
+
7
+ /// The metric passes recurse per tree level (TreeIndex refuses trees deeper than 5,000), so workers
8
+ /// get the stack size of a typical main thread instead of Rust's 2 MiB default.
9
+ const STACK_SIZE: usize = 8 * 1024 * 1024;
10
+
11
+ /// Runs the job on a process-wide pool with one thread per available core. A dedicated pool rather
12
+ /// than libuv's (4 threads by default) lets measurements use every core without the embedding
13
+ /// process having to raise UV_THREADPOOL_SIZE before its first I/O. The pool keeps however many
14
+ /// workers the OS allows (a thread or PID limit may refuse some); with none, the job runs on the
15
+ /// calling thread instead.
16
+ pub fn spawn(job: impl FnOnce() + Send + 'static) {
17
+ static SENDER: OnceLock<Option<mpsc::Sender<Job>>> = OnceLock::new();
18
+ let sender = SENDER.get_or_init(|| {
19
+ let (sender, receiver) = mpsc::channel::<Job>();
20
+ let receiver = Arc::new(Mutex::new(receiver));
21
+ let mut started = 0;
22
+ for _ in 0..thread::available_parallelism().map_or(1, NonZeroUsize::get) {
23
+ let receiver = Arc::clone(&receiver);
24
+ let spawned = thread::Builder::new()
25
+ .name("code-gauge-worker".to_string())
26
+ .stack_size(STACK_SIZE)
27
+ .spawn(move || loop {
28
+ let job = receiver
29
+ .lock()
30
+ .expect("jobs run after the lock is released, so it is never poisoned")
31
+ .recv();
32
+ match job {
33
+ Ok(job) => job(),
34
+ Err(_) => return,
35
+ }
36
+ });
37
+ if spawned.is_err() {
38
+ break;
39
+ }
40
+ started += 1;
41
+ }
42
+ (started > 0).then_some(sender)
43
+ });
44
+ let job: Job = Box::new(job);
45
+ match sender {
46
+ Some(sender) => {
47
+ if let Err(mpsc::SendError(job)) = sender.send(job) {
48
+ job();
49
+ }
50
+ }
51
+ None => job(),
52
+ }
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-gauge",
3
- "version": "4.6.0",
3
+ "version": "4.7.1",
4
4
  "description": "Measure code metrics with tree-sitter.",
5
5
  "keywords": [
6
6
  "cli",
@@ -24,6 +24,7 @@
24
24
  "exports": {
25
25
  ".": {
26
26
  "types": "./dist/index.d.ts",
27
+ "workerd": "./dist/worker.js",
27
28
  "require": "./dist/index.cjs",
28
29
  "import": "./dist/index.js"
29
30
  }
@@ -37,14 +38,17 @@
37
38
  "native/build.rs",
38
39
  "native/Cargo.lock",
39
40
  "native/Cargo.toml",
41
+ "native/code-gauge.wasm",
40
42
  "native/src/",
41
43
  "scripts/buildNative.mjs",
42
- "scripts/installNative.mjs"
44
+ "scripts/installNative.mjs",
45
+ "THIRD-PARTY-NOTICES.txt"
43
46
  ],
44
47
  "scripts": {
45
48
  "benchmark": "node scripts/benchmark.mjs",
46
- "build": "build-ts lib -i src/index.ts -i src/cli.ts",
49
+ "build": "build-ts lib -i src/index.ts -i src/cli.ts -i src/worker.ts --external ../native/code-gauge.wasm",
47
50
  "build-native": "node scripts/buildNative.mjs",
51
+ "build-wasm": "node scripts/buildWasm.mjs",
48
52
  "cleanup": "bun wb lint --fix --format",
49
53
  "format": "bun wb lint --format",
50
54
  "postinstall": "node scripts/installNative.mjs",
@@ -79,7 +83,8 @@
79
83
  "semantic-release": "25.0.5",
80
84
  "sort-package-json": "4.0.0",
81
85
  "typescript": "7.0.2",
82
- "vitest": "4.1.9"
86
+ "vitest": "4.1.9",
87
+ "wrangler": "4.135.0"
83
88
  },
84
89
  "engines": {
85
90
  "node": ">=24"
@@ -89,12 +94,12 @@
89
94
  "registry": "https://registry.npmjs.org/"
90
95
  },
91
96
  "optionalDependencies": {
92
- "code-gauge-linux-x64-gnu": "4.6.0",
93
- "code-gauge-linux-arm64-gnu": "4.6.0",
94
- "code-gauge-linux-x64-musl": "4.6.0",
95
- "code-gauge-linux-arm64-musl": "4.6.0",
96
- "code-gauge-darwin-x64": "4.6.0",
97
- "code-gauge-darwin-arm64": "4.6.0",
98
- "code-gauge-win32-x64-msvc": "4.6.0"
97
+ "code-gauge-linux-x64-gnu": "4.7.1",
98
+ "code-gauge-linux-arm64-gnu": "4.7.1",
99
+ "code-gauge-linux-x64-musl": "4.7.1",
100
+ "code-gauge-linux-arm64-musl": "4.7.1",
101
+ "code-gauge-darwin-x64": "4.7.1",
102
+ "code-gauge-darwin-arm64": "4.7.1",
103
+ "code-gauge-win32-x64-msvc": "4.7.1"
99
104
  }
100
105
  }