@alint-js/core 0.7.0 → 0.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 (2) hide show
  1. package/dist/index.mjs +98 -79
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -14,6 +14,7 @@ import lockfile from "proper-lockfile";
14
14
  import { isNodeErrorCode } from "@alint-js/utils/node";
15
15
  import { errorMessageFrom } from "@moeru/std/error";
16
16
  import { AsyncLocalStorage } from "node:async_hooks";
17
+ import { setImmediate } from "node:timers/promises";
17
18
  import { streamText } from "@xsai/stream-text";
18
19
  //#region src/config/config-array.ts
19
20
  const effectiveBasePathSymbol = Symbol("effectiveBasePath");
@@ -270,7 +271,7 @@ function mergeStopGateConfig(resolved, item) {
270
271
  }
271
272
  //#endregion
272
273
  //#region package.json
273
- var version = "0.7.0";
274
+ var version = "0.7.1";
274
275
  //#endregion
275
276
  //#region src/core/cache/types.ts
276
277
  const CACHE_MAGIC = "ALINT_CACHE";
@@ -2363,88 +2364,14 @@ function projectExecution(runtime) {
2363
2364
  //#endregion
2364
2365
  //#region src/core/source/planner.ts
2365
2366
  /**
2366
- * Converts one rich extractor result into detached descriptors and admits its compact jobs.
2367
- *
2368
- * The returned outcome promise captures only cache ownership and hashes. This function does not
2369
- * await rule execution, so its `SourceFile`, parser output, and rich targets become unreachable
2370
- * when planning returns.
2371
- */
2372
- async function planSource(input, options) {
2373
- if (options.signal?.aborted) return { outcomes: Promise.resolve([]) };
2374
- let file;
2375
- try {
2376
- file = await options.src.readFile(input.path);
2377
- } catch (error) {
2378
- reportFilePlanningComplete(input, 0, options);
2379
- return {
2380
- failure: fileFailure(input, "read", failureMessage(error, "Failed to read source file.")),
2381
- outcomes: Promise.resolve([])
2382
- };
2383
- }
2384
- if (options.signal?.aborted) return { outcomes: Promise.resolve([]) };
2385
- let targets;
2386
- try {
2387
- targets = await input.language.extract(file, {
2388
- cwd: options.cwd,
2389
- languageOptions: input.languageOptions,
2390
- src: options.src
2391
- });
2392
- } catch (error) {
2393
- reportFilePlanningComplete(input, 0, options);
2394
- return {
2395
- failure: fileFailure(input, "extract", failureMessage(error, "Failed to extract source targets.")),
2396
- outcomes: Promise.resolve([])
2397
- };
2398
- }
2399
- const { contentHash } = file;
2400
- let cacheOwner;
2401
- let jobs;
2402
- let project;
2403
- try {
2404
- project = options.projectSnapshots === false ? void 0 : createProjectSnapshot(input, file.language, file.path, contentHash, targets);
2405
- cacheOwner = options.cacheStore.beginOwner({
2406
- kind: "file",
2407
- path: file.path
2408
- }, { contentHash });
2409
- const baseFile = {
2410
- contentHash,
2411
- language: file.language,
2412
- path: file.path
2413
- };
2414
- jobs = createSourceJobs(input, createExecutionTargets(input, targets, baseFile, options.ruleRuntimes, options.cwd, cacheOwner));
2415
- } catch (error) {
2416
- reportFilePlanningComplete(input, 0, options);
2417
- return {
2418
- failure: fileFailure(input, "extract", failureMessage(error, "Failed to plan source targets.")),
2419
- outcomes: Promise.resolve([])
2420
- };
2421
- }
2422
- if (options.signal?.aborted) {
2423
- cacheOwner.commit({ mode: "merge" });
2424
- return {
2425
- outcomes: Promise.resolve([]),
2426
- project
2427
- };
2428
- }
2429
- const batch = options.scheduler.schedule(jobs);
2430
- reportFilePlanningComplete(input, batch.jobsAdded, options);
2431
- return {
2432
- outcomes: batch.outcomes.then(async (settled) => {
2433
- cacheOwner.commit(options.signal?.aborted ? { mode: "merge" } : void 0);
2434
- await options.cacheStore.flush();
2435
- return settled;
2436
- }),
2437
- project
2438
- };
2439
- }
2440
- /**
2441
- * Starts every input plan independently and preserves input-ordered results.
2367
+ * Starts every input read independently, then batches CPU-heavy extraction and job admission.
2442
2368
  */
2443
2369
  async function planSources(inputs, options) {
2444
- return Promise.all(inputs.map((input) => planSource(input, {
2370
+ const waitForPlanning = createPlanningAdmission();
2371
+ return Promise.all(inputs.map((input) => planSourceWithAdmission(input, {
2445
2372
  ...options,
2446
2373
  ruleRuntimes: options.createRuleRuntimes(input)
2447
- })));
2374
+ }, waitForPlanning)));
2448
2375
  }
2449
2376
  function createExecutionTargets(input, targets, file, runtimes, cwd, cacheOwner) {
2450
2377
  const resolveIdentity = createTargetIdentityResolver(targets.map((target) => ({
@@ -2499,6 +2426,28 @@ function createExecutionTargets(input, targets, file, runtimes, cwd, cacheOwner)
2499
2426
  };
2500
2427
  });
2501
2428
  }
2429
+ function createPlanningAdmission() {
2430
+ const batchSize = 32;
2431
+ let admitted = 0;
2432
+ let pendingYield;
2433
+ return async () => {
2434
+ let currentPendingYield = pendingYield;
2435
+ while (currentPendingYield) {
2436
+ await currentPendingYield;
2437
+ currentPendingYield = pendingYield;
2438
+ }
2439
+ admitted += 1;
2440
+ if (admitted < batchSize) return;
2441
+ admitted = 0;
2442
+ const currentYield = setImmediate();
2443
+ pendingYield = currentYield;
2444
+ try {
2445
+ await currentYield;
2446
+ } finally {
2447
+ if (pendingYield === currentYield) pendingYield = void 0;
2448
+ }
2449
+ };
2450
+ }
2502
2451
  function createProjectSnapshot(input, language, path, contentHash, targets) {
2503
2452
  return {
2504
2453
  configHash: input.configHash,
@@ -2570,6 +2519,76 @@ function fileFailure(input, kind, message) {
2570
2519
  message
2571
2520
  };
2572
2521
  }
2522
+ async function planSourceWithAdmission(input, options, waitForPlanning) {
2523
+ if (options.signal?.aborted) return { outcomes: Promise.resolve([]) };
2524
+ let file;
2525
+ try {
2526
+ file = await options.src.readFile(input.path);
2527
+ } catch (error) {
2528
+ reportFilePlanningComplete(input, 0, options);
2529
+ return {
2530
+ failure: fileFailure(input, "read", failureMessage(error, "Failed to read source file.")),
2531
+ outcomes: Promise.resolve([])
2532
+ };
2533
+ }
2534
+ if (options.signal?.aborted) return { outcomes: Promise.resolve([]) };
2535
+ await waitForPlanning?.();
2536
+ if (options.signal?.aborted) return { outcomes: Promise.resolve([]) };
2537
+ let targets;
2538
+ try {
2539
+ targets = await input.language.extract(file, {
2540
+ cwd: options.cwd,
2541
+ languageOptions: input.languageOptions,
2542
+ src: options.src
2543
+ });
2544
+ } catch (error) {
2545
+ reportFilePlanningComplete(input, 0, options);
2546
+ return {
2547
+ failure: fileFailure(input, "extract", failureMessage(error, "Failed to extract source targets.")),
2548
+ outcomes: Promise.resolve([])
2549
+ };
2550
+ }
2551
+ const { contentHash } = file;
2552
+ let cacheOwner;
2553
+ let jobs;
2554
+ let project;
2555
+ try {
2556
+ project = options.projectSnapshots === false ? void 0 : createProjectSnapshot(input, file.language, file.path, contentHash, targets);
2557
+ cacheOwner = options.cacheStore.beginOwner({
2558
+ kind: "file",
2559
+ path: file.path
2560
+ }, { contentHash });
2561
+ const baseFile = {
2562
+ contentHash,
2563
+ language: file.language,
2564
+ path: file.path
2565
+ };
2566
+ jobs = createSourceJobs(input, createExecutionTargets(input, targets, baseFile, options.ruleRuntimes, options.cwd, cacheOwner));
2567
+ } catch (error) {
2568
+ reportFilePlanningComplete(input, 0, options);
2569
+ return {
2570
+ failure: fileFailure(input, "extract", failureMessage(error, "Failed to plan source targets.")),
2571
+ outcomes: Promise.resolve([])
2572
+ };
2573
+ }
2574
+ if (options.signal?.aborted) {
2575
+ cacheOwner.commit({ mode: "merge" });
2576
+ return {
2577
+ outcomes: Promise.resolve([]),
2578
+ project
2579
+ };
2580
+ }
2581
+ const batch = options.scheduler.schedule(jobs);
2582
+ reportFilePlanningComplete(input, batch.jobsAdded, options);
2583
+ return {
2584
+ outcomes: batch.outcomes.then(async (settled) => {
2585
+ cacheOwner.commit(options.signal?.aborted ? { mode: "merge" } : void 0);
2586
+ await options.cacheStore.flush();
2587
+ return settled;
2588
+ }),
2589
+ project
2590
+ };
2591
+ }
2573
2592
  function reportFilePlanningComplete(input, jobsAdded, options) {
2574
2593
  try {
2575
2594
  const progress = options.scheduler.completeFilePlanning();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alint-js/core",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.7.1",
5
5
  "description": "Public rule and plugin SDK and run engine for alint",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -56,7 +56,7 @@
56
56
  "pathe": "^2.0.3",
57
57
  "proper-lockfile": "4.1.2",
58
58
  "valibot": "^1.4.2",
59
- "@alint-js/utils": "0.7.0"
59
+ "@alint-js/utils": "0.7.1"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/proper-lockfile": "4.1.4",