deslop-js 0.0.17 → 0.0.19-dev.42704ea

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/dist/index.cjs CHANGED
@@ -35,6 +35,10 @@ let node_fs_promises = require("node:fs/promises");
35
35
  let oxc_parser = require("oxc-parser");
36
36
  let typescript = require("typescript");
37
37
  typescript = __toESM(typescript, 1);
38
+ let node_worker_threads = require("node:worker_threads");
39
+ let node_url = require("node:url");
40
+ let node_os = require("node:os");
41
+ node_os = __toESM(node_os, 1);
38
42
  let oxc_resolver = require("oxc-resolver");
39
43
  let minimatch = require("minimatch");
40
44
 
@@ -5715,7 +5719,8 @@ const FRAMEWORK_PATTERNS = [
5715
5719
  alwaysUsed: [
5716
5720
  "astro.config.{ts,js,mjs,cjs}",
5717
5721
  "src/content/config.{js,ts,mjs,mts,cjs,cts}",
5718
- "src/content.config.{js,ts,mjs,mts,cjs,cts}"
5722
+ "src/content.config.{js,ts,mjs,mts,cjs,cts}",
5723
+ "src/live.config.{js,ts,mjs,mts,cjs,cts}"
5719
5724
  ]
5720
5725
  },
5721
5726
  {
@@ -6141,6 +6146,148 @@ const discoverToolingEntryPoints = (rootDir, workspacePackages) => {
6141
6146
  };
6142
6147
  };
6143
6148
 
6149
+ //#endregion
6150
+ //#region src/utils/resolve-available-concurrency.ts
6151
+ const resolveAvailableConcurrency = () => {
6152
+ const available = node_os.default.availableParallelism();
6153
+ if (!Number.isFinite(available) || available < 1) return 1;
6154
+ return Math.max(1, Math.min(Math.floor(available), 16));
6155
+ };
6156
+
6157
+ //#endregion
6158
+ //#region src/collect/parallel-parse.ts
6159
+ const deserializeErrors = (serializedErrors) => serializedErrors.map((errorJson) => new DeslopError({
6160
+ code: errorJson.code,
6161
+ module: errorJson.module,
6162
+ severity: errorJson.severity,
6163
+ message: errorJson.message,
6164
+ path: errorJson.path,
6165
+ detail: errorJson.detail
6166
+ }));
6167
+ const deserializeParsedSource = (serialized) => ({
6168
+ imports: serialized.imports,
6169
+ exports: serialized.exports,
6170
+ memberAccesses: serialized.memberAccesses,
6171
+ wholeObjectUses: serialized.wholeObjectUses,
6172
+ localIdentifierReferences: serialized.localIdentifierReferences,
6173
+ referencedFilenames: serialized.referencedFilenames,
6174
+ redundantTypePatterns: serialized.redundantTypePatterns,
6175
+ identityWrappers: serialized.identityWrappers,
6176
+ typeDefinitionHashes: serialized.typeDefinitionHashes,
6177
+ inlineTypeLiterals: serialized.inlineTypeLiterals,
6178
+ simplifiableFunctions: serialized.simplifiableFunctions,
6179
+ simplifiableExpressions: serialized.simplifiableExpressions,
6180
+ duplicateConstantCandidates: serialized.duplicateConstantCandidates,
6181
+ errors: deserializeErrors(serialized.errors)
6182
+ });
6183
+ const resolveWorkerPath = () => {
6184
+ const currentUrl = require("url").pathToFileURL(__filename).href;
6185
+ if (currentUrl.endsWith(".ts")) return (0, node_url.fileURLToPath)(new URL("./parse-worker.ts", currentUrl));
6186
+ return (0, node_url.fileURLToPath)(new URL("./parse-worker.mjs", currentUrl));
6187
+ };
6188
+ const createWorker = (workerPath) => {
6189
+ return new node_worker_threads.Worker(workerPath, { ...workerPath.endsWith(".ts") ? { execArgv: ["--import", "tsx"] } : {} });
6190
+ };
6191
+ const waitForReady = (worker) => new Promise((resolve, reject) => {
6192
+ const onMessage = (message) => {
6193
+ if (message.type === "ready") {
6194
+ worker.off("message", onMessage);
6195
+ worker.off("error", onError);
6196
+ resolve();
6197
+ }
6198
+ };
6199
+ const onError = (error) => {
6200
+ worker.off("message", onMessage);
6201
+ worker.off("error", onError);
6202
+ reject(error);
6203
+ };
6204
+ worker.on("message", onMessage);
6205
+ worker.on("error", onError);
6206
+ });
6207
+ const parseFilesWithWorkerPool = async (files, workerCount) => {
6208
+ const workerPath = resolveWorkerPath();
6209
+ const results = new Array(files.length);
6210
+ const workers = [];
6211
+ try {
6212
+ for (let workerIndex = 0; workerIndex < workerCount; workerIndex++) workers.push(createWorker(workerPath));
6213
+ await Promise.all(workers.map(waitForReady));
6214
+ } catch {
6215
+ for (const worker of workers) worker.terminate();
6216
+ return files.map((file) => parseSourceFile(file.path));
6217
+ }
6218
+ let nextFileIndex = 0;
6219
+ let completedCount = 0;
6220
+ return new Promise((resolve, reject) => {
6221
+ const dispatchNext = (worker) => {
6222
+ if (nextFileIndex >= files.length) return;
6223
+ const fileIndex = nextFileIndex;
6224
+ nextFileIndex += 1;
6225
+ worker.postMessage({
6226
+ type: "parse",
6227
+ filePath: files[fileIndex].path,
6228
+ fileIndex
6229
+ });
6230
+ };
6231
+ const onWorkerMessage = (worker) => (message) => {
6232
+ if (message.type === "result") {
6233
+ results[message.fileIndex] = deserializeParsedSource(message.parsed);
6234
+ completedCount += 1;
6235
+ if (completedCount === files.length) {
6236
+ cleanup();
6237
+ resolve(results);
6238
+ } else dispatchNext(worker);
6239
+ } else if (message.type === "error") {
6240
+ results[message.fileIndex] = {
6241
+ imports: [],
6242
+ exports: [],
6243
+ memberAccesses: [],
6244
+ wholeObjectUses: [],
6245
+ localIdentifierReferences: [],
6246
+ referencedFilenames: [],
6247
+ redundantTypePatterns: [],
6248
+ identityWrappers: [],
6249
+ typeDefinitionHashes: [],
6250
+ inlineTypeLiterals: [],
6251
+ simplifiableFunctions: [],
6252
+ simplifiableExpressions: [],
6253
+ duplicateConstantCandidates: [],
6254
+ errors: [new ParseError({
6255
+ code: "parse-failed",
6256
+ message: `Worker parse failed: ${message.errorMessage}`,
6257
+ path: message.filePath
6258
+ })]
6259
+ };
6260
+ completedCount += 1;
6261
+ if (completedCount === files.length) {
6262
+ cleanup();
6263
+ resolve(results);
6264
+ } else dispatchNext(worker);
6265
+ }
6266
+ };
6267
+ const cleanup = () => {
6268
+ for (const worker of workers) worker.terminate();
6269
+ };
6270
+ for (const worker of workers) {
6271
+ worker.on("message", onWorkerMessage(worker));
6272
+ worker.on("error", (error) => {
6273
+ cleanup();
6274
+ reject(error);
6275
+ });
6276
+ }
6277
+ for (const worker of workers) dispatchNext(worker);
6278
+ });
6279
+ };
6280
+ const parseFilesInParallel = async (files) => {
6281
+ if (files.length <= 50) return files.map((file) => parseSourceFile(file.path));
6282
+ const concurrency = resolveAvailableConcurrency();
6283
+ if (concurrency <= 1) return files.map((file) => parseSourceFile(file.path));
6284
+ try {
6285
+ return await parseFilesWithWorkerPool(files, concurrency);
6286
+ } catch {
6287
+ return files.map((file) => parseSourceFile(file.path));
6288
+ }
6289
+ };
6290
+
6144
6291
  //#endregion
6145
6292
  //#region src/utils/is-platform-builtin-or-virtual.ts
6146
6293
  const BUILTIN_SUBPATH_NODE_MODULES = new Set([
@@ -12590,8 +12737,23 @@ const analyze = async (config) => {
12590
12737
  ignorePatterns: [...config.ignorePatterns, ...allExclusionPatterns]
12591
12738
  } : config;
12592
12739
  let files;
12740
+ let discoveredEntries;
12593
12741
  try {
12594
- files = await collectSourceFiles(configWithExclusions);
12742
+ const [collectedFiles, resolvedEntries] = await Promise.all([collectSourceFiles(configWithExclusions), resolveEntries(configWithExclusions).catch((entriesError) => {
12743
+ setupErrors.push(new WorkspaceError({
12744
+ code: "workspace-discovery-failed",
12745
+ message: "resolveEntries failed — defaulting to empty entry set",
12746
+ path: config.rootDir,
12747
+ detail: describeUnknownError(entriesError)
12748
+ }));
12749
+ return {
12750
+ productionEntries: [],
12751
+ testEntries: [],
12752
+ alwaysUsedFiles: []
12753
+ };
12754
+ })]);
12755
+ files = collectedFiles;
12756
+ discoveredEntries = resolvedEntries;
12595
12757
  } catch (collectError) {
12596
12758
  setupErrors.push(new WorkspaceError({
12597
12759
  code: "workspace-discovery-failed",
@@ -12602,22 +12764,6 @@ const analyze = async (config) => {
12602
12764
  }));
12603
12765
  return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
12604
12766
  }
12605
- let discoveredEntries;
12606
- try {
12607
- discoveredEntries = await resolveEntries(configWithExclusions);
12608
- } catch (entriesError) {
12609
- setupErrors.push(new WorkspaceError({
12610
- code: "workspace-discovery-failed",
12611
- message: "resolveEntries failed — defaulting to empty entry set",
12612
- path: config.rootDir,
12613
- detail: describeUnknownError(entriesError)
12614
- }));
12615
- discoveredEntries = {
12616
- productionEntries: [],
12617
- testEntries: [],
12618
- alwaysUsedFiles: []
12619
- };
12620
- }
12621
12767
  const productionEntrySet = new Set(discoveredEntries.productionEntries);
12622
12768
  const testEntrySet = new Set(discoveredEntries.testEntries);
12623
12769
  const alwaysUsedFileSet = new Set(discoveredEntries.alwaysUsedFiles);
@@ -12644,9 +12790,11 @@ const analyze = async (config) => {
12644
12790
  }));
12645
12791
  return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
12646
12792
  }
12793
+ const parsedModules = await parseFilesInParallel(files);
12647
12794
  const graphInputs = [];
12648
- for (const file of files) {
12649
- const parsedModule = parseSourceFile(file.path);
12795
+ for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
12796
+ const file = files[fileIndex];
12797
+ const parsedModule = parsedModules[fileIndex];
12650
12798
  const resolvedImportMap = /* @__PURE__ */ new Map();
12651
12799
  const safeResolveImport = (specifier) => {
12652
12800
  try {
package/dist/index.mjs CHANGED
@@ -4,6 +4,9 @@ import fg from "fast-glob";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { parseSync } from "oxc-parser";
6
6
  import ts from "typescript";
7
+ import { Worker } from "node:worker_threads";
8
+ import { fileURLToPath } from "node:url";
9
+ import os from "node:os";
7
10
  import { ResolverFactory } from "oxc-resolver";
8
11
  import { minimatch } from "minimatch";
9
12
 
@@ -5684,7 +5687,8 @@ const FRAMEWORK_PATTERNS = [
5684
5687
  alwaysUsed: [
5685
5688
  "astro.config.{ts,js,mjs,cjs}",
5686
5689
  "src/content/config.{js,ts,mjs,mts,cjs,cts}",
5687
- "src/content.config.{js,ts,mjs,mts,cjs,cts}"
5690
+ "src/content.config.{js,ts,mjs,mts,cjs,cts}",
5691
+ "src/live.config.{js,ts,mjs,mts,cjs,cts}"
5688
5692
  ]
5689
5693
  },
5690
5694
  {
@@ -6110,6 +6114,148 @@ const discoverToolingEntryPoints = (rootDir, workspacePackages) => {
6110
6114
  };
6111
6115
  };
6112
6116
 
6117
+ //#endregion
6118
+ //#region src/utils/resolve-available-concurrency.ts
6119
+ const resolveAvailableConcurrency = () => {
6120
+ const available = os.availableParallelism();
6121
+ if (!Number.isFinite(available) || available < 1) return 1;
6122
+ return Math.max(1, Math.min(Math.floor(available), 16));
6123
+ };
6124
+
6125
+ //#endregion
6126
+ //#region src/collect/parallel-parse.ts
6127
+ const deserializeErrors = (serializedErrors) => serializedErrors.map((errorJson) => new DeslopError({
6128
+ code: errorJson.code,
6129
+ module: errorJson.module,
6130
+ severity: errorJson.severity,
6131
+ message: errorJson.message,
6132
+ path: errorJson.path,
6133
+ detail: errorJson.detail
6134
+ }));
6135
+ const deserializeParsedSource = (serialized) => ({
6136
+ imports: serialized.imports,
6137
+ exports: serialized.exports,
6138
+ memberAccesses: serialized.memberAccesses,
6139
+ wholeObjectUses: serialized.wholeObjectUses,
6140
+ localIdentifierReferences: serialized.localIdentifierReferences,
6141
+ referencedFilenames: serialized.referencedFilenames,
6142
+ redundantTypePatterns: serialized.redundantTypePatterns,
6143
+ identityWrappers: serialized.identityWrappers,
6144
+ typeDefinitionHashes: serialized.typeDefinitionHashes,
6145
+ inlineTypeLiterals: serialized.inlineTypeLiterals,
6146
+ simplifiableFunctions: serialized.simplifiableFunctions,
6147
+ simplifiableExpressions: serialized.simplifiableExpressions,
6148
+ duplicateConstantCandidates: serialized.duplicateConstantCandidates,
6149
+ errors: deserializeErrors(serialized.errors)
6150
+ });
6151
+ const resolveWorkerPath = () => {
6152
+ const currentUrl = import.meta.url;
6153
+ if (currentUrl.endsWith(".ts")) return fileURLToPath(new URL("./parse-worker.ts", currentUrl));
6154
+ return fileURLToPath(new URL("./parse-worker.mjs", currentUrl));
6155
+ };
6156
+ const createWorker = (workerPath) => {
6157
+ return new Worker(workerPath, { ...workerPath.endsWith(".ts") ? { execArgv: ["--import", "tsx"] } : {} });
6158
+ };
6159
+ const waitForReady = (worker) => new Promise((resolve, reject) => {
6160
+ const onMessage = (message) => {
6161
+ if (message.type === "ready") {
6162
+ worker.off("message", onMessage);
6163
+ worker.off("error", onError);
6164
+ resolve();
6165
+ }
6166
+ };
6167
+ const onError = (error) => {
6168
+ worker.off("message", onMessage);
6169
+ worker.off("error", onError);
6170
+ reject(error);
6171
+ };
6172
+ worker.on("message", onMessage);
6173
+ worker.on("error", onError);
6174
+ });
6175
+ const parseFilesWithWorkerPool = async (files, workerCount) => {
6176
+ const workerPath = resolveWorkerPath();
6177
+ const results = new Array(files.length);
6178
+ const workers = [];
6179
+ try {
6180
+ for (let workerIndex = 0; workerIndex < workerCount; workerIndex++) workers.push(createWorker(workerPath));
6181
+ await Promise.all(workers.map(waitForReady));
6182
+ } catch {
6183
+ for (const worker of workers) worker.terminate();
6184
+ return files.map((file) => parseSourceFile(file.path));
6185
+ }
6186
+ let nextFileIndex = 0;
6187
+ let completedCount = 0;
6188
+ return new Promise((resolve, reject) => {
6189
+ const dispatchNext = (worker) => {
6190
+ if (nextFileIndex >= files.length) return;
6191
+ const fileIndex = nextFileIndex;
6192
+ nextFileIndex += 1;
6193
+ worker.postMessage({
6194
+ type: "parse",
6195
+ filePath: files[fileIndex].path,
6196
+ fileIndex
6197
+ });
6198
+ };
6199
+ const onWorkerMessage = (worker) => (message) => {
6200
+ if (message.type === "result") {
6201
+ results[message.fileIndex] = deserializeParsedSource(message.parsed);
6202
+ completedCount += 1;
6203
+ if (completedCount === files.length) {
6204
+ cleanup();
6205
+ resolve(results);
6206
+ } else dispatchNext(worker);
6207
+ } else if (message.type === "error") {
6208
+ results[message.fileIndex] = {
6209
+ imports: [],
6210
+ exports: [],
6211
+ memberAccesses: [],
6212
+ wholeObjectUses: [],
6213
+ localIdentifierReferences: [],
6214
+ referencedFilenames: [],
6215
+ redundantTypePatterns: [],
6216
+ identityWrappers: [],
6217
+ typeDefinitionHashes: [],
6218
+ inlineTypeLiterals: [],
6219
+ simplifiableFunctions: [],
6220
+ simplifiableExpressions: [],
6221
+ duplicateConstantCandidates: [],
6222
+ errors: [new ParseError({
6223
+ code: "parse-failed",
6224
+ message: `Worker parse failed: ${message.errorMessage}`,
6225
+ path: message.filePath
6226
+ })]
6227
+ };
6228
+ completedCount += 1;
6229
+ if (completedCount === files.length) {
6230
+ cleanup();
6231
+ resolve(results);
6232
+ } else dispatchNext(worker);
6233
+ }
6234
+ };
6235
+ const cleanup = () => {
6236
+ for (const worker of workers) worker.terminate();
6237
+ };
6238
+ for (const worker of workers) {
6239
+ worker.on("message", onWorkerMessage(worker));
6240
+ worker.on("error", (error) => {
6241
+ cleanup();
6242
+ reject(error);
6243
+ });
6244
+ }
6245
+ for (const worker of workers) dispatchNext(worker);
6246
+ });
6247
+ };
6248
+ const parseFilesInParallel = async (files) => {
6249
+ if (files.length <= 50) return files.map((file) => parseSourceFile(file.path));
6250
+ const concurrency = resolveAvailableConcurrency();
6251
+ if (concurrency <= 1) return files.map((file) => parseSourceFile(file.path));
6252
+ try {
6253
+ return await parseFilesWithWorkerPool(files, concurrency);
6254
+ } catch {
6255
+ return files.map((file) => parseSourceFile(file.path));
6256
+ }
6257
+ };
6258
+
6113
6259
  //#endregion
6114
6260
  //#region src/utils/is-platform-builtin-or-virtual.ts
6115
6261
  const BUILTIN_SUBPATH_NODE_MODULES = new Set([
@@ -12559,8 +12705,23 @@ const analyze = async (config) => {
12559
12705
  ignorePatterns: [...config.ignorePatterns, ...allExclusionPatterns]
12560
12706
  } : config;
12561
12707
  let files;
12708
+ let discoveredEntries;
12562
12709
  try {
12563
- files = await collectSourceFiles(configWithExclusions);
12710
+ const [collectedFiles, resolvedEntries] = await Promise.all([collectSourceFiles(configWithExclusions), resolveEntries(configWithExclusions).catch((entriesError) => {
12711
+ setupErrors.push(new WorkspaceError({
12712
+ code: "workspace-discovery-failed",
12713
+ message: "resolveEntries failed — defaulting to empty entry set",
12714
+ path: config.rootDir,
12715
+ detail: describeUnknownError(entriesError)
12716
+ }));
12717
+ return {
12718
+ productionEntries: [],
12719
+ testEntries: [],
12720
+ alwaysUsedFiles: []
12721
+ };
12722
+ })]);
12723
+ files = collectedFiles;
12724
+ discoveredEntries = resolvedEntries;
12564
12725
  } catch (collectError) {
12565
12726
  setupErrors.push(new WorkspaceError({
12566
12727
  code: "workspace-discovery-failed",
@@ -12571,22 +12732,6 @@ const analyze = async (config) => {
12571
12732
  }));
12572
12733
  return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
12573
12734
  }
12574
- let discoveredEntries;
12575
- try {
12576
- discoveredEntries = await resolveEntries(configWithExclusions);
12577
- } catch (entriesError) {
12578
- setupErrors.push(new WorkspaceError({
12579
- code: "workspace-discovery-failed",
12580
- message: "resolveEntries failed — defaulting to empty entry set",
12581
- path: config.rootDir,
12582
- detail: describeUnknownError(entriesError)
12583
- }));
12584
- discoveredEntries = {
12585
- productionEntries: [],
12586
- testEntries: [],
12587
- alwaysUsedFiles: []
12588
- };
12589
- }
12590
12735
  const productionEntrySet = new Set(discoveredEntries.productionEntries);
12591
12736
  const testEntrySet = new Set(discoveredEntries.testEntries);
12592
12737
  const alwaysUsedFileSet = new Set(discoveredEntries.alwaysUsedFiles);
@@ -12613,9 +12758,11 @@ const analyze = async (config) => {
12613
12758
  }));
12614
12759
  return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
12615
12760
  }
12761
+ const parsedModules = await parseFilesInParallel(files);
12616
12762
  const graphInputs = [];
12617
- for (const file of files) {
12618
- const parsedModule = parseSourceFile(file.path);
12763
+ for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
12764
+ const file = files[fileIndex];
12765
+ const parsedModule = parsedModules[fileIndex];
12619
12766
  const resolvedImportMap = /* @__PURE__ */ new Map();
12620
12767
  const safeResolveImport = (specifier) => {
12621
12768
  try {