veryfront 0.1.1102 → 0.1.1104

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 (126) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/agent/ag-ui/tool-shared.d.ts.map +1 -1
  3. package/esm/src/agent/ag-ui/tool-shared.js +0 -4
  4. package/esm/src/agent/factory.d.ts.map +1 -1
  5. package/esm/src/agent/factory.js +29 -31
  6. package/esm/src/agent/hosted/chat-preparation.d.ts +1 -0
  7. package/esm/src/agent/hosted/chat-preparation.d.ts.map +1 -1
  8. package/esm/src/agent/hosted/chat-preparation.js +20 -12
  9. package/esm/src/agent/hosted/default-project-steering-refresh.d.ts +1 -1
  10. package/esm/src/agent/hosted/default-project-steering-refresh.d.ts.map +1 -1
  11. package/esm/src/agent/hosted/default-project-steering-refresh.js +5 -9
  12. package/esm/src/agent/hosted/runtime-essential-tools.d.ts +1 -1
  13. package/esm/src/agent/hosted/runtime-essential-tools.d.ts.map +1 -1
  14. package/esm/src/agent/hosted/runtime-essential-tools.js +14 -5
  15. package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts.map +1 -1
  16. package/esm/src/agent/hosted/veryfront-cloud-agent-service.js +4 -0
  17. package/esm/src/agent/index.d.ts +1 -1
  18. package/esm/src/agent/index.js +1 -1
  19. package/esm/src/agent/runtime/agent-definition.d.ts.map +1 -1
  20. package/esm/src/agent/runtime/agent-definition.js +3 -0
  21. package/esm/src/agent/runtime/agent-runtime-step.js +1 -1
  22. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  23. package/esm/src/agent/runtime/index.js +0 -2
  24. package/esm/src/agent/runtime/skill-metadata.d.ts +11 -0
  25. package/esm/src/agent/runtime/skill-metadata.d.ts.map +1 -1
  26. package/esm/src/agent/runtime/skill-metadata.js +24 -0
  27. package/esm/src/agent/runtime/skill-prompt.d.ts.map +1 -1
  28. package/esm/src/agent/runtime/skill-prompt.js +5 -2
  29. package/esm/src/agent/types.d.ts +8 -6
  30. package/esm/src/agent/types.d.ts.map +1 -1
  31. package/esm/src/channels/control-plane.d.ts.map +1 -1
  32. package/esm/src/channels/control-plane.js +1 -4
  33. package/esm/src/data/helpers.d.ts +20 -3
  34. package/esm/src/data/helpers.d.ts.map +1 -1
  35. package/esm/src/data/helpers.js +46 -12
  36. package/esm/src/data/static-data-fetcher.d.ts.map +1 -1
  37. package/esm/src/data/static-data-fetcher.js +15 -0
  38. package/esm/src/extensions/builtin-extensions.d.ts +1 -1
  39. package/esm/src/extensions/builtin-extensions.d.ts.map +1 -1
  40. package/esm/src/extensions/builtin-extensions.js +1 -6
  41. package/esm/src/extensions/builtin-schema-validator.d.ts +3 -0
  42. package/esm/src/extensions/builtin-schema-validator.d.ts.map +1 -0
  43. package/esm/src/extensions/builtin-schema-validator.js +8 -0
  44. package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
  45. package/esm/src/html/hydration-script-builder/templates/renderer.js +52 -28
  46. package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
  47. package/esm/src/internal-agents/run-stream.js +6 -11
  48. package/esm/src/internal-agents/run-system-prompt.d.ts +1 -1
  49. package/esm/src/internal-agents/run-system-prompt.js +1 -1
  50. package/esm/src/modules/server/module-server.d.ts.map +1 -1
  51. package/esm/src/modules/server/module-server.js +15 -9
  52. package/esm/src/rendering/orchestrator/module-loader/build-failure.d.ts +20 -0
  53. package/esm/src/rendering/orchestrator/module-loader/build-failure.d.ts.map +1 -0
  54. package/esm/src/rendering/orchestrator/module-loader/build-failure.js +26 -0
  55. package/esm/src/rendering/orchestrator/module-loader/index.d.ts +2 -1
  56. package/esm/src/rendering/orchestrator/module-loader/index.d.ts.map +1 -1
  57. package/esm/src/rendering/orchestrator/module-loader/index.js +69 -7
  58. package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts +6 -0
  59. package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts.map +1 -1
  60. package/esm/src/rendering/orchestrator/module-loader/module-persistence.js +49 -0
  61. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  62. package/esm/src/rendering/orchestrator/pipeline.js +10 -1
  63. package/esm/src/routing/api/context-builder.d.ts +26 -15
  64. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  65. package/esm/src/routing/api/context-builder.js +34 -15
  66. package/esm/src/routing/api/module-loader/loader.js +57 -1
  67. package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
  68. package/esm/src/server/dev-server/middleware.js +29 -6
  69. package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
  70. package/esm/src/server/handlers/request/ssr/ssr.handler.js +6 -4
  71. package/esm/src/skill/prompt-augmentation.d.ts +4 -2
  72. package/esm/src/skill/prompt-augmentation.d.ts.map +1 -1
  73. package/esm/src/skill/prompt-augmentation.js +13 -2
  74. package/esm/src/transforms/esm/import-attributes.d.ts +32 -0
  75. package/esm/src/transforms/esm/import-attributes.d.ts.map +1 -0
  76. package/esm/src/transforms/esm/import-attributes.js +116 -0
  77. package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
  78. package/esm/src/transforms/esm/import-parser.js +68 -18
  79. package/esm/src/transforms/esm/lexer.d.ts +18 -0
  80. package/esm/src/transforms/esm/lexer.d.ts.map +1 -1
  81. package/esm/src/transforms/esm/lexer.js +21 -0
  82. package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
  83. package/esm/src/transforms/esm/specifier-resolver.js +13 -0
  84. package/esm/src/transforms/esm/transform-utils.d.ts +8 -0
  85. package/esm/src/transforms/esm/transform-utils.d.ts.map +1 -1
  86. package/esm/src/transforms/esm/transform-utils.js +11 -1
  87. package/esm/src/transforms/import-rewriter/project-paths.d.ts +26 -0
  88. package/esm/src/transforms/import-rewriter/project-paths.d.ts.map +1 -0
  89. package/esm/src/transforms/import-rewriter/project-paths.js +49 -0
  90. package/esm/src/transforms/import-rewriter/strategies/alias-strategy.d.ts +0 -1
  91. package/esm/src/transforms/import-rewriter/strategies/alias-strategy.d.ts.map +1 -1
  92. package/esm/src/transforms/import-rewriter/strategies/alias-strategy.js +5 -20
  93. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +1 -1
  94. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -1
  95. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +62 -12
  96. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
  97. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +19 -3
  98. package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.d.ts +11 -0
  99. package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.d.ts.map +1 -1
  100. package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.js +13 -0
  101. package/esm/src/transforms/import-rewriter/strategies/relative-strategy.d.ts +0 -1
  102. package/esm/src/transforms/import-rewriter/strategies/relative-strategy.d.ts.map +1 -1
  103. package/esm/src/transforms/import-rewriter/strategies/relative-strategy.js +2 -16
  104. package/esm/src/transforms/import-rewriter/strategies/veryfront-strategy.d.ts.map +1 -1
  105. package/esm/src/transforms/import-rewriter/strategies/veryfront-strategy.js +4 -3
  106. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +4 -2
  107. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
  108. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -7
  109. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts.map +1 -1
  110. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.js +5 -0
  111. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts +25 -3
  112. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts.map +1 -1
  113. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.js +285 -44
  114. package/esm/src/transforms/pipeline/stages/compile.d.ts.map +1 -1
  115. package/esm/src/transforms/pipeline/stages/compile.js +6 -1
  116. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +9 -5
  117. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
  118. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +23 -9
  119. package/esm/src/transforms/shared/server-only-packages.d.ts +30 -0
  120. package/esm/src/transforms/shared/server-only-packages.d.ts.map +1 -0
  121. package/esm/src/transforms/shared/server-only-packages.js +47 -0
  122. package/esm/src/types/entities/getEntityInfo.d.ts.map +1 -1
  123. package/esm/src/types/entities/getEntityInfo.js +10 -2
  124. package/esm/src/utils/version-constant.d.ts +1 -1
  125. package/esm/src/utils/version-constant.js +1 -1
  126. package/package.json +5 -5
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Import attribute rewrites anchored to lexer-reported positions.
3
+ *
4
+ * Every edit here is bounded by a range es-module-lexer reported for a real
5
+ * import statement, so module source embedded in a string literal (the
6
+ * `#deno-config` stub, the generated RSC bundles) is never touched. A plain
7
+ * regex over the module text cannot make that distinction.
8
+ *
9
+ * @module transforms/esm/import-attributes
10
+ */
11
+ /**
12
+ * Rewrite the withdrawn `assert` spelling of an import attribute clause to
13
+ * `with`, for both the static and the dynamic form.
14
+ *
15
+ * esbuild treats `import-assertions` as a feature separate from
16
+ * `import-attributes` and drops the clause when the configured target does not
17
+ * claim it, which turns a working JSON import into "Attempted to load JSON
18
+ * module without specifying \"type\": \"json\"" at load time. Preserving the
19
+ * clause verbatim is no better: Node 22 and Deno 2 removed the keyword, so the
20
+ * only output that runs anywhere is `with`.
21
+ */
22
+ export declare function upgradeImportAssertions(code: string): Promise<string>;
23
+ /**
24
+ * Drop `with { type: "json" }` from every import whose specifier the caller
25
+ * accepts, leaving all other attributes in place.
26
+ *
27
+ * Only a lone JSON type attribute is removed. An import that declares anything
28
+ * else is describing something this rewrite knows nothing about, so it is left
29
+ * exactly as written.
30
+ */
31
+ export declare function stripJsonImportAttributes(code: string, shouldStrip: (specifier: string) => boolean): Promise<string>;
32
+ //# sourceMappingURL=import-attributes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-attributes.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-attributes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgEH;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB3E;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,GAC1C,OAAO,CAAC,MAAM,CAAC,CAmBjB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Import attribute rewrites anchored to lexer-reported positions.
3
+ *
4
+ * Every edit here is bounded by a range es-module-lexer reported for a real
5
+ * import statement, so module source embedded in a string literal (the
6
+ * `#deno-config` stub, the generated RSC bundles) is never touched. A plain
7
+ * regex over the module text cannot make that distinction.
8
+ *
9
+ * @module transforms/esm/import-attributes
10
+ */
11
+ import { parseMaskedImports } from "./lexer.js";
12
+ const ASSERT_KEYWORD = "assert";
13
+ /**
14
+ * The legacy `assert` keyword following a static import specifier.
15
+ *
16
+ * es-module-lexer 2 no longer models assertions, so it ends the statement at
17
+ * the specifier and the clause sits immediately after it. Matching is confined
18
+ * to the specifier's own line, which is what the withdrawn grammar required.
19
+ */
20
+ const STATIC_ASSERT_KEYWORD = /[^\S\r\n]*assert(?=[\s{])/y;
21
+ /** The legacy `assert` key opening the options argument of a dynamic import. */
22
+ const DYNAMIC_ASSERT_KEY = /^\s*,\s*\{\s*assert(?=\s*:)/;
23
+ /** A `with` clause on a static import, capturing the attribute object. */
24
+ const STATIC_WITH_CLAUSE = /^\s*with\s*(\{[^{}]*\})\s*$/;
25
+ /** The options argument of a dynamic import, capturing the attribute object. */
26
+ const DYNAMIC_WITH_ARGUMENT = /^\s*,\s*\{\s*with\s*:\s*(\{[^{}]*\})\s*,?\s*\}\s*$/;
27
+ /** An attribute object declaring a JSON module type and nothing else. */
28
+ const JSON_TYPE_ATTRIBUTE = /^\{\s*(["']?)type\1\s*:\s*(["'])json\2\s*,?\s*\}$/;
29
+ /**
30
+ * The range holding everything a specifier declares after its own text: the
31
+ * `with` clause of a static import, or the options argument of a dynamic one.
32
+ *
33
+ * The range is empty when the import declares no attributes.
34
+ */
35
+ function attributeRange(imp) {
36
+ // Static: `e` indexes the closing quote and `se` ends the statement before
37
+ // any semicolon, so the clause and its keyword lie between them.
38
+ if (imp.d === -1)
39
+ return { start: imp.e + 1, end: imp.se };
40
+ // Dynamic: `e` is already past the closing quote and `se` is past the
41
+ // closing paren, so the comma and the options argument lie between them.
42
+ return { start: imp.e, end: imp.se - 1 };
43
+ }
44
+ /** Whether the declared attributes are exactly a JSON module type. */
45
+ function declaresJsonTypeOnly(clause, isDynamic) {
46
+ const attributes = (isDynamic ? DYNAMIC_WITH_ARGUMENT : STATIC_WITH_CLAUSE).exec(clause)?.[1];
47
+ return attributes !== undefined && JSON_TYPE_ATTRIBUTE.test(attributes);
48
+ }
49
+ /** Position of the legacy `assert` keyword this import uses, or `null`. */
50
+ function findAssertKeyword(masked, imp) {
51
+ if (imp.d === -1) {
52
+ STATIC_ASSERT_KEYWORD.lastIndex = imp.se;
53
+ const match = STATIC_ASSERT_KEYWORD.exec(masked);
54
+ return match === null ? null : STATIC_ASSERT_KEYWORD.lastIndex - ASSERT_KEYWORD.length;
55
+ }
56
+ const { start, end } = attributeRange(imp);
57
+ if (start >= end)
58
+ return null;
59
+ const match = DYNAMIC_ASSERT_KEY.exec(masked.slice(start, end));
60
+ return match === null ? null : start + match[0].length - ASSERT_KEYWORD.length;
61
+ }
62
+ /**
63
+ * Rewrite the withdrawn `assert` spelling of an import attribute clause to
64
+ * `with`, for both the static and the dynamic form.
65
+ *
66
+ * esbuild treats `import-assertions` as a feature separate from
67
+ * `import-attributes` and drops the clause when the configured target does not
68
+ * claim it, which turns a working JSON import into "Attempted to load JSON
69
+ * module without specifying \"type\": \"json\"" at load time. Preserving the
70
+ * clause verbatim is no better: Node 22 and Deno 2 removed the keyword, so the
71
+ * only output that runs anywhere is `with`.
72
+ */
73
+ export async function upgradeImportAssertions(code) {
74
+ if (!code.includes(ASSERT_KEYWORD))
75
+ return code;
76
+ const { masked, imports, unmask } = await parseMaskedImports(code);
77
+ let result = masked;
78
+ for (let i = imports.length - 1; i >= 0; i--) {
79
+ const imp = imports[i];
80
+ if (!imp?.n)
81
+ continue;
82
+ const keyword = findAssertKeyword(masked, imp);
83
+ if (keyword === null)
84
+ continue;
85
+ result = result.slice(0, keyword) + "with" + result.slice(keyword + ASSERT_KEYWORD.length);
86
+ }
87
+ return unmask(result);
88
+ }
89
+ /**
90
+ * Drop `with { type: "json" }` from every import whose specifier the caller
91
+ * accepts, leaving all other attributes in place.
92
+ *
93
+ * Only a lone JSON type attribute is removed. An import that declares anything
94
+ * else is describing something this rewrite knows nothing about, so it is left
95
+ * exactly as written.
96
+ */
97
+ export async function stripJsonImportAttributes(code, shouldStrip) {
98
+ if (!code.includes("with"))
99
+ return code;
100
+ const { masked, imports, unmask } = await parseMaskedImports(code);
101
+ let result = masked;
102
+ for (let i = imports.length - 1; i >= 0; i--) {
103
+ const imp = imports[i];
104
+ if (!imp?.n)
105
+ continue;
106
+ if (!shouldStrip(unmask(imp.n)))
107
+ continue;
108
+ const { start, end } = attributeRange(imp);
109
+ if (start >= end)
110
+ continue;
111
+ if (!declaresJsonTypeOnly(masked.slice(start, end), imp.d > -1))
112
+ continue;
113
+ result = result.slice(0, start) + result.slice(end);
114
+ }
115
+ return unmask(result);
116
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CA0HlC"}
1
+ {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAwCD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CA2HlC"}
@@ -1,6 +1,8 @@
1
1
  import { compileContent } from "../mdx/compiler/index.js";
2
2
  import { getEsbuild } from "../../platform/compat/esbuild.js";
3
- import { join } from "../../platform/compat/path/index.js";
3
+ import { dirname, join, relative } from "../../platform/compat/path/index.js";
4
+ import { computeHash } from "../../utils/hash-utils.js";
5
+ import { LRUCache } from "../../utils/lru-wrapper.js";
4
6
  import { createFileSystem } from "../../platform/compat/fs.js";
5
7
  import { isFrameworkSourcePath, resolveRelativeFrameworkSourceImport, } from "../../platform/compat/framework-source-resolver.js";
6
8
  import { isCrossProjectImport, parseCrossProjectImport } from "./path-resolver.js";
@@ -8,19 +10,43 @@ import { parseImports } from "./lexer.js";
8
10
  import { getLoaderFromPath } from "./transform-utils.js";
9
11
  const EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mdx"];
10
12
  const HAS_EXTENSION_RE = /\.(tsx?|jsx?|mjs|cjs|mdx|css)$/;
13
+ /**
14
+ * Compiled MDX, keyed by project, file and content hash.
15
+ *
16
+ * Dependency parsing runs on every render, including every memory, Redis and
17
+ * MDX-ESM cache hit, and recurses through the dependency tree. Without this the
18
+ * full remark/rehype compile of every MDX file is paid again on each of them,
19
+ * for a result that cannot change while the content does not.
20
+ */
21
+ const COMPILED_MDX_CACHE_MAX_ENTRIES = 200;
22
+ const compiledMdxCache = new LRUCache({
23
+ maxEntries: COMPILED_MDX_CACHE_MAX_ENTRIES,
24
+ });
25
+ async function compileMdxForParsing(code, filePath, projectDir) {
26
+ const cacheKey = `${projectDir}::${filePath}::${await computeHash(code)}`;
27
+ const cached = compiledMdxCache.get(cacheKey);
28
+ if (cached !== undefined)
29
+ return cached;
30
+ const compiled = await compileContent("development", projectDir, code, undefined, filePath, "server");
31
+ compiledMdxCache.set(cacheKey, compiled.compiledCode);
32
+ return compiled.compiledCode;
33
+ }
11
34
  export async function parseLocalImports(code, filePath, projectDir, adapter) {
12
- if (filePath.endsWith(".css") || filePath.endsWith(".json")) {
35
+ // Markdown compiles to a fixed template whose only import is the bare JSX
36
+ // runtime, which this parser discards, so the answer for a `.md` file is
37
+ // always "no dependencies". Compiling one to learn that is pure cost on a
38
+ // path that runs per render.
39
+ if (filePath.endsWith(".css") || filePath.endsWith(".json") || /\.md$/i.test(filePath)) {
13
40
  return { imports: [], cssImports: [], crossProjectImports: [], missing: [] };
14
41
  }
15
- // MDX/Markdown is not JSX, so handing the raw source to esbuild under the
16
- // `jsx` loader fails with "<stdin>:1:1: ERROR: Syntax error" — which surfaced
17
- // to users as "Component has missing dependencies" for a file that exists.
18
- // Compile content to JSX first, exactly as the transform pipeline's parse
19
- // stage does, then read the imports out of that.
42
+ // MDX is not JSX, so handing the raw source to esbuild under the `jsx` loader
43
+ // fails with "<stdin>:1:1: ERROR: Syntax error", which surfaced to users as
44
+ // "Component has missing dependencies" for a file that exists. Compile
45
+ // content to JSX first, exactly as the transform pipeline's parse stage does,
46
+ // then read the imports out of that.
20
47
  let parseSource = code;
21
- if (/\.mdx?$/i.test(filePath)) {
22
- const compiled = await compileContent("development", projectDir, code, undefined, filePath, "server");
23
- parseSource = compiled.compiledCode;
48
+ if (/\.mdx$/i.test(filePath)) {
49
+ parseSource = await compileMdxForParsing(code, filePath, projectDir);
24
50
  }
25
51
  const esbuild = await getEsbuild();
26
52
  const result = await esbuild.transform(parseSource, {
@@ -49,19 +75,24 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
49
75
  // below and are dropped without even being reported as missing, so an MDX
50
76
  // file's sibling components are never recursively transformed.
51
77
  if (specifier.startsWith("file://")) {
52
- const absolutePath = fileUrlToPath(specifier);
53
- if (absolutePath && await checkFileExists(absolutePath, adapter)) {
54
- const entry = { specifier, absolutePath };
55
- if (absolutePath.endsWith(".css"))
78
+ const targetPath = fileUrlToPath(specifier);
79
+ // A rewritten specifier carries a server path the author never wrote, and
80
+ // this record is read back verbatim in the "Component has missing
81
+ // dependencies" build error. Report what the author wrote instead.
82
+ const authoredSpecifier = toAuthoredSpecifier(targetPath, specifier, filePath);
83
+ const resolved = targetPath ? await resolveExistingFilePath(targetPath, adapter) : null;
84
+ if (resolved) {
85
+ const entry = { specifier: authoredSpecifier, absolutePath: resolved };
86
+ if (resolved.endsWith(".css"))
56
87
  cssImports.push(entry);
57
88
  else
58
89
  localImports.push(entry);
59
90
  continue;
60
91
  }
61
92
  missingImports.push({
62
- specifier,
93
+ specifier: authoredSpecifier,
63
94
  fromFile: filePath,
64
- reason: `File not found: ${absolutePath ?? specifier}`,
95
+ reason: `File not found: tried extensions ${EXTENSIONS.join(", ")}`,
65
96
  });
66
97
  continue;
67
98
  }
@@ -116,6 +147,17 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
116
147
  }
117
148
  return { imports: localImports, cssImports, crossProjectImports, missing: missingImports };
118
149
  }
150
+ /**
151
+ * The specifier as the author most likely wrote it, reconstructed from the
152
+ * absolute path a compile step rewrote it to. Falls back to the file name when
153
+ * the URL cannot be read, so no server path escapes into a user-facing report.
154
+ */
155
+ function toAuthoredSpecifier(targetPath, specifier, fromFile) {
156
+ if (!targetPath)
157
+ return `./${specifier.slice(specifier.lastIndexOf("/") + 1)}`;
158
+ const relativePath = relative(dirname(fromFile), targetPath);
159
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
160
+ }
119
161
  /** Filesystem path behind a `file://` specifier, or null when it is not one. */
120
162
  function fileUrlToPath(specifier) {
121
163
  try {
@@ -147,7 +189,15 @@ async function resolveLocalImportPath(fromFile, importSpecifier, adapter) {
147
189
  return resolvedFrameworkImport;
148
190
  }
149
191
  const fromDir = fromFile.substring(0, fromFile.lastIndexOf("/"));
150
- const basePath = resolveRelative(fromDir, importSpecifier);
192
+ return await resolveExistingFilePath(resolveRelative(fromDir, importSpecifier), adapter);
193
+ }
194
+ /**
195
+ * Path of the file a local import points at: the adapter's own resolution
196
+ * first, then the extension and directory-index probes. Every local import
197
+ * shape resolves through here, so an extensionless or directory specifier
198
+ * behaves the same however it reached this module.
199
+ */
200
+ async function resolveExistingFilePath(basePath, adapter) {
151
201
  if (adapter?.fs.resolveFile) {
152
202
  try {
153
203
  const normalizedPath = basePath.replace(/^\/+/, "");
@@ -160,7 +210,7 @@ async function resolveLocalImportPath(fromFile, importSpecifier, adapter) {
160
210
  // Fall through to traditional resolution
161
211
  }
162
212
  }
163
- if (HAS_EXTENSION_RE.test(importSpecifier)) {
213
+ if (HAS_EXTENSION_RE.test(basePath)) {
164
214
  return (await checkFileExists(basePath, adapter)) ? basePath : null;
165
215
  }
166
216
  for (const ext of EXTENSIONS) {
@@ -2,6 +2,24 @@ import type { ImportSpecifier } from "../../extensions/bundler/module-lexer.js";
2
2
  export type { ImportSpecifier };
3
3
  export declare function initLexer(): Promise<void>;
4
4
  export declare function parseImports(code: string): Promise<readonly ImportSpecifier[]>;
5
+ /** A parse whose positions index into a masked copy of the source. */
6
+ export interface MaskedParse {
7
+ /** The source with HTTP URLs replaced by fixed-width placeholders. */
8
+ masked: string;
9
+ /** Specifiers whose every positional field indexes into {@link masked}. */
10
+ imports: readonly ImportSpecifier[];
11
+ /** Restore the masked HTTP URLs in a string derived from {@link masked}. */
12
+ unmask: (text: string) => string;
13
+ }
14
+ /**
15
+ * Parse imports and hand back the masked source the positions belong to.
16
+ *
17
+ * Masking changes offsets, so `imp.s`, `imp.a` and friends are meaningless
18
+ * against the original text. Callers that splice by position must edit
19
+ * `masked` and run the result through `unmask`; callers that only need
20
+ * specifier names should use {@link parseImports} instead.
21
+ */
22
+ export declare function parseMaskedImports(code: string): Promise<MaskedParse>;
5
23
  /**
6
24
  * Replace import specifiers (the path string) in the code.
7
25
  * Safe for simple re-mappings like aliases or rewriting URLs.
@@ -1 +1 @@
1
- {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/lexer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,0CAA0C,CAAC;AAE7F,YAAY,EAAE,eAAe,EAAE,CAAC;AAuDhC,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAS/C;AAyBD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAqBpF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7E,OAAO,CAAC,MAAM,CAAC,CAoCjB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GACnE,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/lexer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,0CAA0C,CAAC;AAE7F,YAAY,EAAE,eAAe,EAAE,CAAC;AAuDhC,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAS/C;AAyBD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAqBpF;AAED,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,4EAA4E;IAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAc3E;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7E,OAAO,CAAC,MAAM,CAAC,CAoCjB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GACnE,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
@@ -86,6 +86,27 @@ export async function parseImports(code) {
86
86
  return restoredN === imp.n ? imp : { ...imp, n: restoredN };
87
87
  });
88
88
  }
89
+ /**
90
+ * Parse imports and hand back the masked source the positions belong to.
91
+ *
92
+ * Masking changes offsets, so `imp.s`, `imp.a` and friends are meaningless
93
+ * against the original text. Callers that splice by position must edit
94
+ * `masked` and run the result through `unmask`; callers that only need
95
+ * specifier names should use {@link parseImports} instead.
96
+ */
97
+ export async function parseMaskedImports(code) {
98
+ await initLexer();
99
+ const { masked, urlMap } = maskHttpUrls(code);
100
+ let imports;
101
+ try {
102
+ imports = getLexer().parse(masked);
103
+ }
104
+ catch (error) {
105
+ logParseError(error, masked);
106
+ throw error;
107
+ }
108
+ return { masked, imports, unmask: (text) => unmaskHttpUrls(text, urlMap) };
109
+ }
89
110
  /**
90
111
  * Replace import specifiers (the path string) in the code.
91
112
  * Safe for simple re-mappings like aliases or rewriting URLs.
@@ -1 +1 @@
1
- {"version":3,"file":"specifier-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/specifier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,EACL,KAAK,YAAY,EAQlB,MAAM,yBAAyB,CAAC;AAEjC,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAoH/F,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAuChC;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,eAAe,CAAC,CAa1B"}
1
+ {"version":3,"file":"specifier-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/specifier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,EACL,KAAK,YAAY,EAQlB,MAAM,yBAAyB,CAAC;AAEjC,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AA+H/F,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAuChC;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,eAAe,CAAC,CAa1B"}
@@ -9,6 +9,8 @@
9
9
  import { basename } from "../../platform/compat/path/index.js";
10
10
  import { resolveImport } from "../../modules/import-map/resolver.js";
11
11
  import { rendererLogger } from "../../utils/index.js";
12
+ import { parseBarePackageSpecifier } from "../shared/package-specifier.js";
13
+ import { isServerOnlyPackage } from "../shared/server-only-packages.js";
12
14
  import { parseImports, replaceSpecifiers } from "./lexer.js";
13
15
  const logger = rendererLogger.component("specifier-resolver");
14
16
  import { isCanonicalReactEsmUrl, isExternalScheme, isHttpUrl, isInternalBare, isParentHttpModule, isRelative, resolveBareSpecifier, } from "./http-cache-helpers.js";
@@ -26,6 +28,17 @@ function isLocalMappedSpecifier(specifier) {
26
28
  async function resolveSpecifier(specifier, baseUrl, options, cacheHttpModule) {
27
29
  if (isExternalScheme(specifier))
28
30
  return null;
31
+ // Server-only packages (`redis`, `pg`, …), including their explicit `npm:`
32
+ // form, must never be routed through esm.sh. esm.sh either 500s building them
33
+ // or emits a browser bundle with Node built-ins stubbed that can never
34
+ // connect. The framework's adapters only `import()` them behind a lazy,
35
+ // configured code path, so leaving the specifier external lets the runtime
36
+ // resolve the real package (node_modules on Node, npm: on Deno) if and when
37
+ // the backend is actually used — and costs nothing when it is not.
38
+ const serverOnlyCandidate = specifier.startsWith("npm:") ? specifier.slice(4) : specifier;
39
+ const serverOnlyParsed = parseBarePackageSpecifier(serverOnlyCandidate);
40
+ if (serverOnlyParsed && isServerOnlyPackage(serverOnlyParsed.packageName))
41
+ return null;
29
42
  if (isInternalBare(specifier)) {
30
43
  const mapped = resolveImport(specifier, options.importMap);
31
44
  if (mapped === specifier)
@@ -12,9 +12,17 @@ export declare function computeShortContentHash(content: string): Promise<string
12
12
  * that consumes this output requires the attribute to load a JSON module, so
13
13
  * dropping it turns a working import into a load-time error:
14
14
  * `Attempted to load JSON module without specifying "type": "json"`.
15
+ *
16
+ * `import-assertions` is the separate feature key esbuild uses for the
17
+ * withdrawn `assert { type: "json" }` spelling of the same clause. It is
18
+ * enabled for the same reason, so the clause reaches the output instead of
19
+ * vanishing. The output must not keep that spelling though, because Node 22
20
+ * and Deno 2 removed the keyword, so every consumer of these options runs the
21
+ * result through `upgradeImportAssertions` to rewrite it to `with`.
15
22
  */
16
23
  export declare const ESBUILD_SUPPORTED_FEATURES: {
17
24
  readonly "import-attributes": true;
25
+ readonly "import-assertions": true;
18
26
  };
19
27
  export declare function getLoaderFromPath(filePath: string): Loader;
20
28
  export declare function needsTransform(filePath: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"transform-utils.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/transform-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAGhE;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAExE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;;CAAyC,CAAC;AAajF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD"}
1
+ {"version":3,"file":"transform-utils.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/transform-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAGhE;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAExE;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,0BAA0B;;;CAG7B,CAAC;AAaX,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -14,8 +14,18 @@ export function computeShortContentHash(content) {
14
14
  * that consumes this output requires the attribute to load a JSON module, so
15
15
  * dropping it turns a working import into a load-time error:
16
16
  * `Attempted to load JSON module without specifying "type": "json"`.
17
+ *
18
+ * `import-assertions` is the separate feature key esbuild uses for the
19
+ * withdrawn `assert { type: "json" }` spelling of the same clause. It is
20
+ * enabled for the same reason, so the clause reaches the output instead of
21
+ * vanishing. The output must not keep that spelling though, because Node 22
22
+ * and Deno 2 removed the keyword, so every consumer of these options runs the
23
+ * result through `upgradeImportAssertions` to rewrite it to `with`.
17
24
  */
18
- export const ESBUILD_SUPPORTED_FEATURES = { "import-attributes": true };
25
+ export const ESBUILD_SUPPORTED_FEATURES = {
26
+ "import-attributes": true,
27
+ "import-assertions": true,
28
+ };
19
29
  const EXTENSION_LOADERS = {
20
30
  ".tsx": "tsx",
21
31
  ".ts": "ts",
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Project-relative path helpers shared by the rewrite strategies.
3
+ *
4
+ * Several strategies need the path of the file being transformed expressed
5
+ * relative to the project root: the alias and relative strategies to build
6
+ * module URLs, the asset strategy to name the importer in its message.
7
+ */
8
+ /**
9
+ * Path of `filePath` relative to `projectDir`, or null when the file is not
10
+ * inside the project.
11
+ *
12
+ * The check is on a path boundary, so `/projectile/src/Header.tsx` is not
13
+ * treated as living inside `/project`.
14
+ */
15
+ export declare function relativeToProjectDir(filePath: string, projectDir: string): string | null;
16
+ /**
17
+ * Best-effort project-relative path, for callers that need a path to build a
18
+ * module URL from and have no useful fallback.
19
+ *
20
+ * A file outside the project can still be a copy of a project file staged
21
+ * somewhere else (a temp bundle directory, for example), so the project
22
+ * directory name is looked up in the path before giving up and returning the
23
+ * input unchanged.
24
+ */
25
+ export declare function getProjectRelativePath(filePath: string, projectDir: string): string;
26
+ //# sourceMappingURL=project-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-paths.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/import-rewriter/project-paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQxF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAanF"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Project-relative path helpers shared by the rewrite strategies.
3
+ *
4
+ * Several strategies need the path of the file being transformed expressed
5
+ * relative to the project root: the alias and relative strategies to build
6
+ * module URLs, the asset strategy to name the importer in its message.
7
+ */
8
+ /** Normalize separators and drop any trailing slash. */
9
+ function normalizeDir(dir) {
10
+ return dir.replace(/\\/g, "/").replace(/\/+$/, "");
11
+ }
12
+ /**
13
+ * Path of `filePath` relative to `projectDir`, or null when the file is not
14
+ * inside the project.
15
+ *
16
+ * The check is on a path boundary, so `/projectile/src/Header.tsx` is not
17
+ * treated as living inside `/project`.
18
+ */
19
+ export function relativeToProjectDir(filePath, projectDir) {
20
+ const normalizedFilePath = filePath.replace(/\\/g, "/");
21
+ const normalizedProjectDir = normalizeDir(projectDir);
22
+ if (normalizedFilePath === normalizedProjectDir)
23
+ return "";
24
+ if (!normalizedFilePath.startsWith(`${normalizedProjectDir}/`))
25
+ return null;
26
+ return normalizedFilePath.slice(normalizedProjectDir.length + 1);
27
+ }
28
+ /**
29
+ * Best-effort project-relative path, for callers that need a path to build a
30
+ * module URL from and have no useful fallback.
31
+ *
32
+ * A file outside the project can still be a copy of a project file staged
33
+ * somewhere else (a temp bundle directory, for example), so the project
34
+ * directory name is looked up in the path before giving up and returning the
35
+ * input unchanged.
36
+ */
37
+ export function getProjectRelativePath(filePath, projectDir) {
38
+ const direct = relativeToProjectDir(filePath, projectDir);
39
+ if (direct !== null)
40
+ return direct;
41
+ if (!filePath.startsWith("/"))
42
+ return filePath;
43
+ const pathParts = filePath.split("/");
44
+ const lastProjectPart = normalizeDir(projectDir).split("/").at(-1);
45
+ const projectIndex = lastProjectPart ? pathParts.indexOf(lastProjectPart) : -1;
46
+ if (projectIndex >= 0)
47
+ return pathParts.slice(projectIndex + 1).join("/");
48
+ return filePath;
49
+ }
@@ -4,7 +4,6 @@ export declare class AliasStrategy implements ImportRewriteStrategy {
4
4
  readonly priority = 1;
5
5
  matches(specifier: string, _ctx: RewriteContext): boolean;
6
6
  rewrite(info: ImportSpecifierInfo, ctx: RewriteContext): RewriteResult;
7
- private getRelativeFilePath;
8
7
  }
9
8
  export declare const aliasStrategy: AliasStrategy;
10
9
  //# sourceMappingURL=alias-strategy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"alias-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/import-rewriter/strategies/alias-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAGrB,qBAAa,aAAc,YAAW,qBAAqB;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,KAAK;IAEtB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAIzD,OAAO,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa;IAyCtE,OAAO,CAAC,mBAAmB;CAoB5B;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
1
+ {"version":3,"file":"alias-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/import-rewriter/strategies/alias-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAIrB,qBAAa,aAAc,YAAW,qBAAqB;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,KAAK;IAEtB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAIzD,OAAO,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa;CAwCvE;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { normalizeExtension } from "../url-builder.js";
2
+ import { getProjectRelativePath } from "../project-paths.js";
2
3
  export class AliasStrategy {
3
4
  name = "alias";
4
5
  priority = 1;
@@ -11,7 +12,7 @@ export class AliasStrategy {
11
12
  if (ctx.target === "ssr") {
12
13
  let normalizedPath = normalizeExtension(path);
13
14
  // Add .js if no extension present
14
- if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css|js)$/.test(normalizedPath)) {
15
+ if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css)$/.test(normalizedPath)) {
15
16
  normalizedPath = `${normalizedPath}.js`;
16
17
  }
17
18
  return { specifier: `/_vf_modules/${normalizedPath}` };
@@ -22,38 +23,22 @@ export class AliasStrategy {
22
23
  // but module path is "_vf_modules/components/elements/Textarea.js").
23
24
  if (ctx.moduleServerUrl) {
24
25
  let normalizedPath = normalizeExtension(path);
25
- if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css|js)$/.test(normalizedPath)) {
26
+ if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css)$/.test(normalizedPath)) {
26
27
  normalizedPath = `${normalizedPath}.js`;
27
28
  }
28
29
  return { specifier: `${ctx.moduleServerUrl}/${normalizedPath}` };
29
30
  }
30
31
  // Fallback: Use relative paths when no module server is configured.
31
32
  // This is used for local development without a module server.
32
- const relativeFilePath = this.getRelativeFilePath(ctx.filePath, ctx.projectDir);
33
+ const relativeFilePath = getProjectRelativePath(ctx.filePath, ctx.projectDir);
33
34
  const fileDir = relativeFilePath.substring(0, relativeFilePath.lastIndexOf("/"));
34
35
  const depth = fileDir.split("/").filter(Boolean).length;
35
36
  const prefix = depth === 0 ? "./" : "../".repeat(depth);
36
37
  let relativePath = normalizeExtension(`${prefix}${path}`);
37
- if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css|js)$/.test(relativePath)) {
38
+ if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css)$/.test(relativePath)) {
38
39
  relativePath = `${relativePath}.js`;
39
40
  }
40
41
  return { specifier: relativePath };
41
42
  }
42
- getRelativeFilePath(filePath, projectDir) {
43
- const normalizedProjectDir = projectDir.replace(/\\/g, "/").replace(/\/$/, "");
44
- if (filePath.startsWith(normalizedProjectDir)) {
45
- return filePath.substring(normalizedProjectDir.length + 1);
46
- }
47
- if (!filePath.startsWith("/"))
48
- return filePath;
49
- const pathParts = filePath.split("/");
50
- const projectParts = normalizedProjectDir.split("/");
51
- const lastProjectPart = projectParts.at(-1);
52
- const projectIndex = lastProjectPart ? pathParts.indexOf(lastProjectPart) : -1;
53
- if (projectIndex >= 0) {
54
- return pathParts.slice(projectIndex + 1).join("/");
55
- }
56
- return filePath;
57
- }
58
43
  }
59
44
  export const aliasStrategy = new AliasStrategy();
@@ -1,4 +1,4 @@
1
- import type { ImportRewriteStrategy, ImportSpecifierInfo, RewriteContext, RewriteResult } from "../types.js";
1
+ import { type ImportRewriteStrategy, type ImportSpecifierInfo, type RewriteContext, type RewriteResult } from "../types.js";
2
2
  /**
3
3
  * Reject `import logo from "@/assets/logo.svg"`.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"asset-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/import-rewriter/strategies/asset-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAarB;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,YAAW,qBAAqB;IACzD,QAAQ,CAAC,IAAI,WAAW;IAGxB,QAAQ,CAAC,QAAQ,OAAO;IAExB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAIzD,OAAO,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa;CAiBvE;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
1
+ {"version":3,"file":"asset-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/import-rewriter/strategies/asset-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAkErB;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,YAAW,qBAAqB;IACzD,QAAQ,CAAC,IAAI,WAAW;IAGxB,QAAQ,CAAC,QAAQ,OAAO;IAExB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAWzD,OAAO,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa;CAevE;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}