dist-DKbukT1H.mjs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { Module } from "node:module";
  2. import { MessageChannel } from "node:worker_threads";
  3. import { fileURLToPath } from "node:url";
  4. //#region ../../node_modules/.pnpm/fresh-import@0.2.1/node_modules/fresh-import/dist/index.js
  5. const instanceId = Math.random().toString(36).slice(2);
  6. const relativeImportRE = /^\.{1,2}(?:\/|\\)/;
  7. function escapeRegExp(value) {
  8. return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  9. }
  10. /**
  11. * The tracking query name `fresh-import-<instance>`, where `<instance>` is a
  12. * random value unique to this loaded module instance. Any two instances (even
  13. * two copies of the same build loaded into the same process) get distinct
  14. * names, so each hook only recognizes the imports it tagged itself.
  15. */
  16. function buildQueryName() {
  17. return `fresh-import-${instanceId}`;
  18. }
  19. /**
  20. * Build the regex that matches the tracking query `?<name>=<id>,<context>`
  21. * (or the `&<name>=...` form).
  22. */
  23. function buildQueryRE(queryName) {
  24. return new RegExp(`(?:\\?|&)${escapeRegExp(queryName)}=(\\d+),([^&]+)(?:&|$)`);
  25. }
  26. /**
  27. * Build the tracking query `?<name>=<id>,<context>` that `collect` appends to
  28. * the entry specifier. `id` cache-busts the import (a distinct URL forces a
  29. * fresh evaluation) and `context` tags the import graph so the resolve hook can
  30. * attribute resolved dependencies back to the originating collect.
  31. */
  32. function formatTrackingQuery(queryName, id, context) {
  33. return `?${queryName}=${id},${context}`;
  34. }
  35. /**
  36. * Shared body of the resolve hook for both the on-thread and off-thread
  37. * importers. Given an already-resolved `result`, decides whether it is a tracked
  38. * relative file dependency; if so, reports it via `onDependency` and tags the
  39. * URL so the query propagates to its own dependencies.
  40. *
  41. * The sync/async difference between the two hooks lives entirely in the caller
  42. * (which awaits `nextResolve` or not); this function performs no I/O. `result`
  43. * is mutated in place and returned.
  44. */
  45. function trackResolved(specifier, context, result, queryName, queryRE, onDependency) {
  46. const isRelativeImport = relativeImportRE.test(specifier);
  47. if (result.format === "builtin" || !isRelativeImport) return result;
  48. if (!context.parentURL || queryRE.test(result.url) || !result.url.startsWith("file:")) return result;
  49. const m = queryRE.exec(context.parentURL);
  50. if (m) {
  51. const [, id, contextFile] = m;
  52. onDependency(contextFile, result.url);
  53. result.url = result.url.replace(/(\?)|$/, (_n, n1) => `?${queryName}=${id},${contextFile}${n1 === "?" ? "&" : ""}`);
  54. }
  55. return result;
  56. }
  57. var loader_default = "data:text/javascript,Math.random().toString(36).slice(2);%0Aconst relativeImportRE = /^\\.{1,2}(%3F:\\/|\\\\)/;%0Afunction escapeRegExp(value) {%0A%09return value.replace(/[.*+%3F^${}()|[\\]\\\\]/g, \"\\\\$&\");%0A}%0A/**%0A* Build the regex that matches the tracking query `%3F<name>=<id>,<context>`%0A* (or the `&<name>=...` form).%0A*/%0Afunction buildQueryRE(queryName) {%0A%09return new RegExp(`(%3F:\\\\%3F|&)${escapeRegExp(queryName)}=(\\\\d+),([^&]+)(%3F:&|$)`);%0A}%0A/**%0A* Shared body of the resolve hook for both the on-thread and off-thread%0A* importers. Given an already-resolved `result`, decides whether it is a tracked%0A* relative file dependency; if so, reports it via `onDependency` and tags the%0A* URL so the query propagates to its own dependencies.%0A*%0A* The sync/async difference between the two hooks lives entirely in the caller%0A* (which awaits `nextResolve` or not); this function performs no I/O. `result`%0A* is mutated in place and returned.%0A*/%0Afunction trackResolved(specifier, context, result, queryName, queryRE, onDependency) {%0A%09const isRelativeImport = relativeImportRE.test(specifier);%0A%09if (result.format === \"builtin\" || !isRelativeImport) return result;%0A%09if (!context.parentURL || queryRE.test(result.url) || !result.url.startsWith(\"file:\")) return result;%0A%09const m = queryRE.exec(context.parentURL);%0A%09if (m) {%0A%09%09const [, id, contextFile] = m;%0A%09%09onDependency(contextFile, result.url);%0A%09%09result.url = result.url.replace(/(\\%3F)|$/, (_n, n1) => `%3F${queryName}=${id},${contextFile}${n1 === \"%3F\" %3F \"&\" : \"\"}`);%0A%09}%0A%09return result;%0A}%0A//%23endregion%0A//%23region src/off-thread/loader.ts%0Alet port;%0Alet queryName;%0Alet queryRE;%0Aconst initialize = async (data) => {%0A%09port = data.port;%0A%09queryName = data.queryName;%0A%09queryRE = buildQueryRE(queryName);%0A};%0Aconst resolve = async (specifier, context, nextResolve) => {%0A%09return trackResolved(specifier, context, await nextResolve(specifier, context), queryName, queryRE, (ctx, url) => {%0A%09%09port.postMessage({%0A%09%09%09context: ctx,%0A%09%09%09url%0A%09%09});%0A%09});%0A};%0A//%23endregion%0Aexport { initialize, resolve };%0A";
  58. let nextId$1 = 0;
  59. /**
  60. * Off-thread importer: registers an ESM loader in a worker thread via
  61. * `Module.register` and receives tracked dependencies over a `MessagePort`.
  62. * Used on Node versions without `Module.registerHooks`.
  63. */
  64. function createOffThreadImporter() {
  65. const queryName = buildQueryName();
  66. const { port1, port2 } = new MessageChannel();
  67. Module.register(loader_default, {
  68. data: {
  69. port: port2,
  70. queryName
  71. },
  72. transferList: [port2]
  73. });
  74. port1.unref();
  75. return { async collect(specifier) {
  76. const id = nextId$1++;
  77. const depsList = /* @__PURE__ */ new Set();
  78. const onMessage = (e) => {
  79. if (e.context === specifier) depsList.add(e.url);
  80. };
  81. port1.on("message", onMessage);
  82. port1.unref();
  83. try {
  84. const result = await import(specifier + formatTrackingQuery(queryName, id, specifier));
  85. await new Promise((resolve) => setImmediate(resolve));
  86. return {
  87. result,
  88. dependencies: [...depsList].filter((url) => url.startsWith("file:")).map((url) => fileURLToPath(url))
  89. };
  90. } finally {
  91. port1.off("message", onMessage);
  92. }
  93. } };
  94. }
  95. let nextId = 0;
  96. /**
  97. * On-thread importer: registers synchronous resolution hooks via
  98. * `Module.registerHooks` (Node 22.15+/23.5+).
  99. */
  100. function createOnThreadImporter() {
  101. const registry = /* @__PURE__ */ new Map();
  102. const queryName = buildQueryName();
  103. const queryRE = buildQueryRE(queryName);
  104. const resolve = (specifier, context, nextResolve) => {
  105. return trackResolved(specifier, context, nextResolve(specifier, context), queryName, queryRE, (ctx, url) => {
  106. registry.get(ctx)?.add(url);
  107. });
  108. };
  109. Module.registerHooks({ resolve });
  110. return { async collect(specifier) {
  111. const id = nextId++;
  112. const depsList = /* @__PURE__ */ new Set();
  113. registry.set(specifier, depsList);
  114. try {
  115. return {
  116. result: await import(specifier + formatTrackingQuery(queryName, id, specifier)),
  117. dependencies: [...depsList].filter((url) => url.startsWith("file:")).map((url) => fileURLToPath(url))
  118. };
  119. } finally {
  120. registry.delete(specifier);
  121. }
  122. } };
  123. }
  124. /**
  125. * Create the importer best suited to the current runtime, or `undefined` if it
  126. * provides neither module-hook API.
  127. */
  128. function createImporter() {
  129. if (Module.registerHooks) return createOnThreadImporter();
  130. if (Module.register) return createOffThreadImporter();
  131. }
  132. let importer;
  133. let initialized = false;
  134. /**
  135. * Import an ESM entry in its own fresh module graph (separate from Node's module
  136. * cache and from other concurrent imports) and report the dependency files it
  137. * pulled in.
  138. *
  139. * Each call re-evaluates the entry in a fresh graph; concurrent calls stay
  140. * isolated from one another. Only statically-imported relative dependencies are
  141. * tracked, not dynamic imports.
  142. *
  143. * Returns `undefined` on runtimes that provide neither `Module.registerHooks`
  144. * nor `Module.register`.
  145. */
  146. function freshImport(specifier) {
  147. if (!initialized) {
  148. importer = createImporter();
  149. initialized = true;
  150. }
  151. return importer?.collect(specifier);
  152. }
  153. //#endregion
  154. export { freshImport };