flamerjs 1.0.0 → 1.1.0
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/README.md +63 -32
- package/dist/cli.js +122 -64
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -51,18 +51,22 @@ can be used locally, in pull requests, or in CI.
|
|
|
51
51
|
- **Language-aware checks:** JavaScript/TypeScript, Python, Rust, Go, C/C++,
|
|
52
52
|
C#, Zig, SQL, notebooks, and additional language profiles.
|
|
53
53
|
- **Stable JSON output:** suitable for CI annotations and other automation.
|
|
54
|
-
- **Single-pass scanning:**
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
- **Single-pass TypeScript scanning:** the repository is walked once and
|
|
55
|
+
content is collected once before analyzers run.
|
|
56
|
+
- **Coordinated native workers:** Rust handles shared file metrics and
|
|
57
|
+
Rust-specific checks, while C++ handles C/C++ checks; TypeScript remains the
|
|
58
|
+
source of truth and fallback path.
|
|
57
59
|
|
|
58
60
|
## Requirements
|
|
59
61
|
|
|
60
62
|
- Node.js 18 or newer
|
|
61
63
|
- npm
|
|
62
64
|
|
|
63
|
-
The default TypeScript engine needs only Node.js and npm.
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
The default TypeScript engine needs only Node.js and npm. The published package
|
|
66
|
+
also declares optional, platform-specific native packages containing both
|
|
67
|
+
workers, so a compatible npm install gets full mode automatically. Building
|
|
68
|
+
workers from this repository requires Rust/Cargo and a C++17 compiler with
|
|
69
|
+
CMake.
|
|
66
70
|
|
|
67
71
|
## Quick start
|
|
68
72
|
|
|
@@ -107,24 +111,24 @@ flamerjs /path/to/repository --format summary
|
|
|
107
111
|
flamerjs [path] [options]
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
| Option
|
|
111
|
-
|
|
|
112
|
-
| `--package, --scope <name>`
|
|
113
|
-
| `--path <dir>`
|
|
114
|
-
| `--format <json\|summary>`
|
|
115
|
-
| `--out <file>`
|
|
116
|
-
| `--fail-on <SEVERITY>`
|
|
117
|
-
| `--max-files <n>`
|
|
118
|
-
| `--commit-limit <n>`
|
|
119
|
-
| `--no-git`
|
|
120
|
-
| `--no-lockfile-warning`
|
|
121
|
-
| `--ignore-dir <name>`
|
|
122
|
-
| `--quiet`
|
|
123
|
-
| `--color`
|
|
124
|
-
| `--no-color`
|
|
125
|
-
| `--engine <typescript\|
|
|
126
|
-
| `--list-languages`
|
|
127
|
-
| `--help`
|
|
114
|
+
| Option | Description |
|
|
115
|
+
| --------------------------------------- | ----------------------------------------------------------------------------- |
|
|
116
|
+
| `--package, --scope <name>` | Analyze only a named sub-project |
|
|
117
|
+
| `--path <dir>` | Set the repository root; defaults to the current directory |
|
|
118
|
+
| `--format <json\|summary>` | Select JSON or human-readable output; summary is the default |
|
|
119
|
+
| `--out <file>` | Also write the JSON report to a file |
|
|
120
|
+
| `--fail-on <SEVERITY>` | Exit with status 1 at or above `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, or `INFO` |
|
|
121
|
+
| `--max-files <n>` | Limit the number of files scanned |
|
|
122
|
+
| `--commit-limit <n>` | Limit commits inspected by Git checks |
|
|
123
|
+
| `--no-git` | Skip Git analysis |
|
|
124
|
+
| `--no-lockfile-warning` | Suppress the missing-lockfile suggestion |
|
|
125
|
+
| `--ignore-dir <name>` | Ignore an additional directory name; repeatable |
|
|
126
|
+
| `--quiet` | Suppress the human summary |
|
|
127
|
+
| `--color` | Force ANSI colors |
|
|
128
|
+
| `--no-color` | Disable ANSI colors |
|
|
129
|
+
| `--engine <all\|typescript\|rust\|cpp>` | Run all engines (default) or select one engine |
|
|
130
|
+
| `--list-languages` | List recognized language profiles |
|
|
131
|
+
| `--help` | Show command help |
|
|
128
132
|
|
|
129
133
|
Exit status `0` means the scan completed; findings may still exist. Status `1`
|
|
130
134
|
means `--fail-on` was reached, and status `2` indicates invalid usage or a
|
|
@@ -177,30 +181,57 @@ The project uses Node.js and npm as its supported development toolchain.
|
|
|
177
181
|
`package-lock.json` is the canonical dependency lockfile.
|
|
178
182
|
The current test suite contains **175 tests**.
|
|
179
183
|
|
|
180
|
-
###
|
|
184
|
+
### Native engine
|
|
181
185
|
|
|
182
|
-
Flamer
|
|
183
|
-
|
|
186
|
+
Flamer runs all three components by default: TypeScript analyzers plus the Rust
|
|
187
|
+
and C++ native workers. They do different work and run concurrently:
|
|
188
|
+
|
|
189
|
+
- TypeScript walks the repository, runs the generic analyzers, and produces the
|
|
190
|
+
report.
|
|
191
|
+
- Rust computes shared metrics for all selected files and checks Rust files for
|
|
192
|
+
`.unwrap()` usage.
|
|
193
|
+
- C++ checks only C and C++ files for native-language issues such as
|
|
194
|
+
`#include <bits/stdc++.h>`.
|
|
184
195
|
|
|
185
196
|
```bash
|
|
186
|
-
npm run native:build
|
|
187
|
-
node dist/cli.js .
|
|
197
|
+
npm run native:build:all
|
|
198
|
+
node dist/cli.js .
|
|
188
199
|
```
|
|
189
200
|
|
|
190
|
-
|
|
201
|
+
Native findings are merged into the TypeScript report only after all requested
|
|
202
|
+
native jobs succeed. If a native binary is unavailable or fails, the default
|
|
203
|
+
`all` mode falls back to a pure TypeScript report. Use `--engine typescript` for
|
|
204
|
+
a native-free scan, or `--engine rust` / `--engine cpp` to require one specific
|
|
205
|
+
worker; explicit native modes return an error when their worker fails.
|
|
191
206
|
The native protocol and worker details are documented in
|
|
192
207
|
[`native/core/README.md`](native/core/README.md).
|
|
193
208
|
|
|
194
209
|
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the runtime layers,
|
|
195
210
|
worker protocol, engine selection, and native development workflow.
|
|
196
211
|
|
|
212
|
+
For the source installation path:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
curl -fsSL https://raw.githubusercontent.com/dummy3ye/flamer/master/scripts/install.sh | sh
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Platform-specific native release binaries are not bundled in the current npm
|
|
219
|
+
package yet. The npm package uses optional native packages when they are
|
|
220
|
+
published for the current platform; if npm skips them, TypeScript fallback is
|
|
221
|
+
automatic. The installer's `native` path is reserved for standalone releases
|
|
222
|
+
that attach those assets.
|
|
223
|
+
|
|
197
224
|
## How it works
|
|
198
225
|
|
|
199
226
|
1. Detect the repository layout, languages, and ecosystems.
|
|
200
227
|
2. Walk the tree once and collect file metadata and content-derived signals.
|
|
201
228
|
3. Run isolated analyzers against the collected records.
|
|
202
|
-
4.
|
|
203
|
-
|
|
229
|
+
4. Run the requested native jobs through the versioned JSONL protocol. Native
|
|
230
|
+
jobs receive non-overlapping responsibilities and return metrics or
|
|
231
|
+
language-specific findings.
|
|
232
|
+
5. Merge successful native findings, sort everything, and emit JSON or a
|
|
233
|
+
concise summary. In default `all` mode, any native failure returns to the
|
|
234
|
+
TypeScript-only result.
|
|
204
235
|
|
|
205
236
|
Analyzers are intentionally factual and conservative. A finding should identify
|
|
206
237
|
something actionable rather than merely express a preference.
|
package/dist/cli.js
CHANGED
|
@@ -353,7 +353,8 @@ var CATEGORIES = [
|
|
|
353
353
|
"structure",
|
|
354
354
|
"git",
|
|
355
355
|
"error",
|
|
356
|
-
"unfinished"
|
|
356
|
+
"unfinished",
|
|
357
|
+
"native"
|
|
357
358
|
];
|
|
358
359
|
function rank(severity) {
|
|
359
360
|
const r = RANK[String(severity).toUpperCase()];
|
|
@@ -2902,39 +2903,79 @@ var ANALYZERS = [
|
|
|
2902
2903
|
];
|
|
2903
2904
|
|
|
2904
2905
|
// src/native.ts
|
|
2905
|
-
import {
|
|
2906
|
+
import { spawn } from "child_process";
|
|
2906
2907
|
import { existsSync as existsSync3 } from "fs";
|
|
2907
2908
|
import { join as join6 } from "path";
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
const request = JSON.stringify({ protocol: 1, operation: "file_metrics", root, files });
|
|
2919
|
-
const result = spawnSync(worker, { input: `${request}
|
|
2920
|
-
`, encoding: "utf8", timeout: 3e4 });
|
|
2921
|
-
if (result.error) throw new Error(`native worker failed: ${result.error.message}`);
|
|
2922
|
-
if (result.status !== 0) {
|
|
2923
|
-
throw new Error(`native worker exited with status ${result.status}: ${result.stderr.trim()}`);
|
|
2924
|
-
}
|
|
2925
|
-
let response;
|
|
2909
|
+
import { createRequire } from "module";
|
|
2910
|
+
var require2 = createRequire(import.meta.url);
|
|
2911
|
+
function nativePackageName() {
|
|
2912
|
+
const platform = `${process.platform}-${process.arch}`;
|
|
2913
|
+
const supported = /* @__PURE__ */ new Set(["linux-x64", "darwin-x64", "darwin-arm64", "win32-x64"]);
|
|
2914
|
+
return supported.has(platform) ? `@dummy3ye/flamer-native-${platform}` : null;
|
|
2915
|
+
}
|
|
2916
|
+
function packagedWorkerPath(worker) {
|
|
2917
|
+
const packageName = nativePackageName();
|
|
2918
|
+
if (!packageName) return null;
|
|
2926
2919
|
try {
|
|
2927
|
-
|
|
2920
|
+
return require2.resolve(`${packageName}/${worker}`);
|
|
2928
2921
|
} catch {
|
|
2929
|
-
|
|
2930
|
-
}
|
|
2931
|
-
if (response.protocol !== 1 || response.operation !== "file_metrics") {
|
|
2932
|
-
throw new Error("native worker returned an incompatible response");
|
|
2922
|
+
return null;
|
|
2933
2923
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2924
|
+
}
|
|
2925
|
+
function nativeWorkerPath(root, worker) {
|
|
2926
|
+
const configured = worker === "rust" ? process.env.FLAMER_RUST_CORE : process.env.FLAMER_CPP_CORE;
|
|
2927
|
+
if (configured) return configured;
|
|
2928
|
+
if (process.env.FLAMER_CORE && worker === "rust") return process.env.FLAMER_CORE;
|
|
2929
|
+
const packaged = packagedWorkerPath(worker);
|
|
2930
|
+
if (packaged) return packaged;
|
|
2931
|
+
return worker === "rust" ? join6(root, "native", "core", "rust", "target", "release", "flamer-core") : join6(root, "native", "core", "cpp", "build", "flamer-core-cpp");
|
|
2932
|
+
}
|
|
2933
|
+
function runNativeWorker(root, files, worker, operation = "file_metrics") {
|
|
2934
|
+
const executable = nativeWorkerPath(root, worker);
|
|
2935
|
+
if (!existsSync3(executable)) {
|
|
2936
|
+
return Promise.reject(
|
|
2937
|
+
new Error(`${worker} worker not found at ${executable}; build the native workers first`)
|
|
2938
|
+
);
|
|
2936
2939
|
}
|
|
2937
|
-
|
|
2940
|
+
const request = JSON.stringify({ protocol: 1, operation, root, files });
|
|
2941
|
+
return new Promise((resolve2, reject) => {
|
|
2942
|
+
const child = spawn(executable, { stdio: ["pipe", "pipe", "pipe"] });
|
|
2943
|
+
let stdout = "";
|
|
2944
|
+
let stderr = "";
|
|
2945
|
+
const timeout = setTimeout(() => {
|
|
2946
|
+
child.kill();
|
|
2947
|
+
reject(new Error(`${worker} worker timed out`));
|
|
2948
|
+
}, 3e4);
|
|
2949
|
+
child.stdout.on("data", (chunk) => stdout += chunk);
|
|
2950
|
+
child.stderr.on("data", (chunk) => stderr += chunk);
|
|
2951
|
+
child.on("error", (error) => {
|
|
2952
|
+
clearTimeout(timeout);
|
|
2953
|
+
reject(new Error(`${worker} worker failed: ${error.message}`));
|
|
2954
|
+
});
|
|
2955
|
+
child.on("close", (status) => {
|
|
2956
|
+
clearTimeout(timeout);
|
|
2957
|
+
if (status !== 0) {
|
|
2958
|
+
reject(new Error(`${worker} worker exited with status ${status}: ${stderr.trim()}`));
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
try {
|
|
2962
|
+
const response = JSON.parse(stdout);
|
|
2963
|
+
if (response.protocol !== 1 || response.operation !== operation) {
|
|
2964
|
+
throw new Error(`${worker} worker returned an incompatible response`);
|
|
2965
|
+
}
|
|
2966
|
+
if (!response.ok) {
|
|
2967
|
+
throw new Error(
|
|
2968
|
+
response.errors.map((error) => `${error.path}: ${error.message}`).join("; ")
|
|
2969
|
+
);
|
|
2970
|
+
}
|
|
2971
|
+
resolve2(response);
|
|
2972
|
+
} catch (error) {
|
|
2973
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
2974
|
+
}
|
|
2975
|
+
});
|
|
2976
|
+
child.stdin.end(`${request}
|
|
2977
|
+
`);
|
|
2978
|
+
});
|
|
2938
2979
|
}
|
|
2939
2980
|
|
|
2940
2981
|
// src/cli.ts
|
|
@@ -2954,7 +2995,7 @@ function parseArgs(argv) {
|
|
|
2954
2995
|
listLanguages: false,
|
|
2955
2996
|
help: false,
|
|
2956
2997
|
color: null,
|
|
2957
|
-
engine: "
|
|
2998
|
+
engine: "all",
|
|
2958
2999
|
ignoreDirs: []
|
|
2959
3000
|
};
|
|
2960
3001
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -3001,8 +3042,8 @@ function parseArgs(argv) {
|
|
|
3001
3042
|
opts.color = false;
|
|
3002
3043
|
break;
|
|
3003
3044
|
case "--engine":
|
|
3004
|
-
opts.engine = argv[++i] || "
|
|
3005
|
-
if (!["typescript", "
|
|
3045
|
+
opts.engine = argv[++i] || "all";
|
|
3046
|
+
if (!["all", "typescript", "rust", "cpp"].includes(opts.engine)) {
|
|
3006
3047
|
process.stderr.write(`invalid engine: ${opts.engine}
|
|
3007
3048
|
`);
|
|
3008
3049
|
process.exit(2);
|
|
@@ -3049,8 +3090,8 @@ Options:
|
|
|
3049
3090
|
--quiet suppress the human summary
|
|
3050
3091
|
--color force ANSI colors
|
|
3051
3092
|
--no-color disable ANSI colors
|
|
3052
|
-
--engine <typescript|
|
|
3053
|
-
|
|
3093
|
+
--engine <all|typescript|rust|cpp>
|
|
3094
|
+
all runs TypeScript, Rust, and C++ (default: all)
|
|
3054
3095
|
--list-languages print the supported language table
|
|
3055
3096
|
-h, --help show this help
|
|
3056
3097
|
`);
|
|
@@ -3067,7 +3108,7 @@ function printLanguages() {
|
|
|
3067
3108
|
${allExtensions().length} file extensions recognized.
|
|
3068
3109
|
`);
|
|
3069
3110
|
}
|
|
3070
|
-
function run2(opts) {
|
|
3111
|
+
async function run2(opts) {
|
|
3071
3112
|
const started = Date.now();
|
|
3072
3113
|
const root = opts.root;
|
|
3073
3114
|
if (!existsSync4(root) || !isDir(root)) {
|
|
@@ -3209,35 +3250,52 @@ function run2(opts) {
|
|
|
3209
3250
|
}
|
|
3210
3251
|
};
|
|
3211
3252
|
if (opts.engine !== "typescript") {
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3253
|
+
const allFiles = scopedFiles.map((file) => file.rel);
|
|
3254
|
+
const cppFiles = scopedFiles.filter((file) => ["c", "cpp"].includes(file.lang)).map((file) => file.rel);
|
|
3255
|
+
const rustFiles = scopedFiles.filter((file) => file.lang === "rust").map((file) => file.rel);
|
|
3256
|
+
const jobs = opts.engine === "all" ? [
|
|
3257
|
+
["rust", "file_metrics", allFiles],
|
|
3258
|
+
["rust", "rust_analysis", rustFiles],
|
|
3259
|
+
["cpp", "cpp_analysis", cppFiles]
|
|
3260
|
+
] : opts.engine === "rust" ? [
|
|
3261
|
+
["rust", "file_metrics", allFiles],
|
|
3262
|
+
["rust", "rust_analysis", rustFiles]
|
|
3263
|
+
] : [["cpp", "cpp_analysis", cppFiles]];
|
|
3264
|
+
const nativeResults = {};
|
|
3265
|
+
const failures = [];
|
|
3266
|
+
const nativeFindings = [];
|
|
3267
|
+
await Promise.all(
|
|
3268
|
+
jobs.map(async ([worker, operation, files2]) => {
|
|
3269
|
+
if (!files2.length && operation !== "file_metrics") return;
|
|
3270
|
+
try {
|
|
3271
|
+
const native = await runNativeWorker(root, files2, worker, operation);
|
|
3272
|
+
nativeResults[`${worker}:${operation}`] = {
|
|
3273
|
+
files: native.metrics.files,
|
|
3274
|
+
bytes: native.metrics.bytes,
|
|
3275
|
+
lines: native.metrics.lines,
|
|
3276
|
+
durationMs: native.duration_ms
|
|
3277
|
+
};
|
|
3278
|
+
nativeFindings.push(...native.findings || []);
|
|
3279
|
+
} catch (error) {
|
|
3280
|
+
failures.push(error?.message || String(error));
|
|
3281
|
+
}
|
|
3282
|
+
})
|
|
3283
|
+
);
|
|
3284
|
+
if (failures.length && opts.engine !== "all") {
|
|
3285
|
+
findings.push(
|
|
3286
|
+
finding({
|
|
3287
|
+
category: "error",
|
|
3288
|
+
severity: "CRITICAL",
|
|
3289
|
+
title: "Native engine failed",
|
|
3290
|
+
detail: failures.join("; ")
|
|
3291
|
+
})
|
|
3216
3292
|
);
|
|
3217
|
-
report.
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
bytes: native.metrics.bytes,
|
|
3221
|
-
lines: native.metrics.lines,
|
|
3222
|
-
durationMs: native.duration_ms
|
|
3223
|
-
};
|
|
3224
|
-
} catch (error) {
|
|
3225
|
-
const message = error?.message || String(error);
|
|
3226
|
-
if (opts.engine === "native") {
|
|
3227
|
-
findings.push(
|
|
3228
|
-
finding({
|
|
3229
|
-
category: "error",
|
|
3230
|
-
severity: "CRITICAL",
|
|
3231
|
-
title: "Native engine failed",
|
|
3232
|
-
detail: message
|
|
3233
|
-
})
|
|
3234
|
-
);
|
|
3235
|
-
report.findings = sortFindings(findings);
|
|
3236
|
-
report.metadata.native = { engine: "rust", error: message };
|
|
3237
|
-
return { report, exitCode: 2 };
|
|
3238
|
-
}
|
|
3239
|
-
report.metadata.native = { engine: "typescript", fallback: message };
|
|
3293
|
+
report.findings = sortFindings(findings);
|
|
3294
|
+
report.metadata.native = { engine: opts.engine, errors: failures };
|
|
3295
|
+
return { report, exitCode: 2 };
|
|
3240
3296
|
}
|
|
3297
|
+
if (!failures.length) findings.push(...nativeFindings);
|
|
3298
|
+
report.metadata.native = failures.length ? { engine: "typescript", fallback: failures, workers: nativeResults } : { engine: opts.engine, workers: nativeResults };
|
|
3241
3299
|
}
|
|
3242
3300
|
let exitCode = 0;
|
|
3243
3301
|
if (opts.failOn && report.metadata.worst && rank(report.metadata.worst) <= rank(opts.failOn)) {
|
|
@@ -3362,11 +3420,11 @@ for (const stream of [process.stdout, process.stderr]) {
|
|
|
3362
3420
|
throw err;
|
|
3363
3421
|
});
|
|
3364
3422
|
}
|
|
3365
|
-
function main() {
|
|
3423
|
+
async function main() {
|
|
3366
3424
|
const opts = parseArgs(process.argv.slice(2));
|
|
3367
3425
|
if (opts.help) return printHelp();
|
|
3368
3426
|
if (opts.listLanguages) return printLanguages();
|
|
3369
|
-
const { report, exitCode } = run2(opts);
|
|
3427
|
+
const { report, exitCode } = await run2(opts);
|
|
3370
3428
|
const json = JSON.stringify(report, null, 2);
|
|
3371
3429
|
if (opts.out) {
|
|
3372
3430
|
try {
|
|
@@ -3383,4 +3441,4 @@ function main() {
|
|
|
3383
3441
|
}
|
|
3384
3442
|
process.exitCode = exitCode;
|
|
3385
3443
|
}
|
|
3386
|
-
main();
|
|
3444
|
+
void main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flamerjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Language-agnostic code health auditor that walks a repository once and reports what is wrong with it",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,11 +22,20 @@
|
|
|
22
22
|
"files": [
|
|
23
23
|
"dist"
|
|
24
24
|
],
|
|
25
|
+
"optionalDependencies": {
|
|
26
|
+
"@dummy3ye/flamer-native-darwin-arm64": "1.1.1",
|
|
27
|
+
"@dummy3ye/flamer-native-darwin-x64": "1.1.1",
|
|
28
|
+
"@dummy3ye/flamer-native-linux-x64": "1.1.1",
|
|
29
|
+
"@dummy3ye/flamer-native-win32-x64": "1.1.1"
|
|
30
|
+
},
|
|
25
31
|
"scripts": {
|
|
26
32
|
"build": "tsup",
|
|
27
33
|
"dev": "tsup --watch",
|
|
28
|
-
"native:build": "
|
|
29
|
-
"native:test": "
|
|
34
|
+
"native:build": "sh scripts/native-build.sh",
|
|
35
|
+
"native:test": "sh scripts/native-test.sh",
|
|
36
|
+
"native:build:cpp": "sh scripts/native-build-cpp.sh",
|
|
37
|
+
"native:build:all": "sh scripts/native-build.sh",
|
|
38
|
+
"native:package": "node scripts/package-native.mjs",
|
|
30
39
|
"prepublishOnly": "npm run lint && npm run build && npm test",
|
|
31
40
|
"lint": "eslint .",
|
|
32
41
|
"lint:fix": "eslint . --fix",
|