veryfront 0.1.1095 → 0.1.1098

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/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.1095",
3
+ "version": "0.1.1098",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -1 +1 @@
1
- {"version":3,"file":"esm-rewriter.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/esm-rewriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAWzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAmCrE;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,cAAc,EAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
1
+ {"version":3,"file":"esm-rewriter.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/esm-rewriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAkCzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAmCrE;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,cAAc,EAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CAwEjB"}
@@ -1,7 +1,28 @@
1
1
  import { rendererLogger } from "../../../utils/index.js";
2
2
  import { MODULE_NOT_FOUND } from "../../../errors/index.js";
3
3
  import { generateHash } from "./cache.js";
4
+ import { parseImports } from "../../../transforms/esm/lexer.js";
4
5
  const logger = rendererLogger.component("module-loader");
6
+ /**
7
+ * Specifiers `code` imports statically, as opposed to through `import(...)` or
8
+ * merely mentioning in a string.
9
+ *
10
+ * A lex failure returns every discovered specifier as static, so an unparseable
11
+ * bundle keeps the pre-graceful-degradation behaviour of failing loudly rather
12
+ * than quietly shipping a remote dependency.
13
+ */
14
+ async function staticImportSpecifiers(code) {
15
+ try {
16
+ const imports = await parseImports(code);
17
+ return new Set(imports.filter((imp) => imp.d === -1 && imp.n).map((imp) => imp.n));
18
+ }
19
+ catch (error) {
20
+ logger.debug("Could not lex a fetched module; treating its imports as static", {
21
+ error: error instanceof Error ? error.message : String(error),
22
+ });
23
+ return new Set(code.match(/https:\/\/esm\.sh\/[^"']+/g) ?? []);
24
+ }
25
+ }
5
26
  function escapeRegExp(value) {
6
27
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
28
  }
@@ -53,17 +74,39 @@ export async function fetchEsmModule(url, tmpDir, localAdapter, esmCache) {
53
74
  allEsmUrls.add(match[1]);
54
75
  }
55
76
  const urlArray = Array.from(allEsmUrls);
56
- const cachedPaths = await Promise.all(urlArray.map((esmUrl) => fetchEsmModule(esmUrl, tmpDir, localAdapter, esmCache)));
77
+ const staticUrls = await staticImportSpecifiers(code);
78
+ // Nested pre-fetches of a URL this module only reaches lazily are
79
+ // best-effort: a broken esm.sh build for one package logs a warning and the
80
+ // URL stays in the emitted code for the runtime to resolve at call time. A
81
+ // URL the module imports statically is part of its own import graph and must
82
+ // still resolve here, so the emitted artifact's static dependencies stay
83
+ // local. See `transforms/esm/specifier-resolver.ts` for the same rule on the
84
+ // SSR transform path.
85
+ const settledPaths = await Promise.allSettled(urlArray.map((esmUrl) => fetchEsmModule(esmUrl, tmpDir, localAdapter, esmCache)));
57
86
  if (urlArray.length) {
58
87
  const replacementMap = new Map();
59
88
  for (let i = 0; i < urlArray.length; i++) {
60
89
  const url = urlArray[i];
61
- const cached = cachedPaths[i];
62
- if (url && cached)
63
- replacementMap.set(url, `file://${cached}`);
90
+ const result = settledPaths[i];
91
+ if (!url || !result)
92
+ continue;
93
+ if (result.status === "fulfilled") {
94
+ replacementMap.set(url, `file://${result.value}`);
95
+ continue;
96
+ }
97
+ // A statically imported dependency must be local before this module is
98
+ // handed to the runtime loader, so its failure stays fatal.
99
+ if (staticUrls.has(url))
100
+ throw result.reason;
101
+ logger.warn("Leaving an unfetchable lazy esm.sh module for runtime resolution", {
102
+ url,
103
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
104
+ });
105
+ }
106
+ if (replacementMap.size) {
107
+ const combinedPattern = new RegExp(Array.from(replacementMap.keys()).map(escapeRegExp).join("|"), "g");
108
+ code = code.replace(combinedPattern, (m) => replacementMap.get(m) ?? m);
64
109
  }
65
- const combinedPattern = new RegExp(urlArray.map(escapeRegExp).join("|"), "g");
66
- code = code.replace(combinedPattern, (m) => replacementMap.get(m) ?? m);
67
110
  }
68
111
  const hash = await generateHash(url);
69
112
  const tempFilePath = `${tmpDir}/esm-${hash}.js`;
@@ -147,7 +147,7 @@ export async function loadModule(filePath, config) {
147
147
  const tmpDir = await getModuleCacheDir(config);
148
148
  const localAdapter = await getLocalAdapter();
149
149
  const tempFilePath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
150
- const moduleUrl = `file://${tempFilePath}?t=${Date.now()}`;
150
+ const moduleUrl = `file://${tempFilePath}`;
151
151
  try {
152
152
  return await import(moduleUrl);
153
153
  }
@@ -11,6 +11,9 @@ import { type CacheOptions } from "./http-cache-helpers.js";
11
11
  export type CacheHttpModuleFn = (url: string, options: CacheOptions) => Promise<string | null>;
12
12
  /**
13
13
  * Build a map of specifier replacements by resolving all imports in the code.
14
+ *
15
+ * Resolution failure is fatal for a static import and best-effort for a
16
+ * specifier only ever used in `import(...)`. See {@link isDynamicOnly}.
14
17
  */
15
18
  export declare function buildReplacements(code: string, baseUrl: string | undefined, options: CacheOptions, cacheHttpModule: CacheHttpModuleFn): Promise<Map<string, string>>;
16
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"specifier-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/specifier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,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;AAgF/F;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiB9B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,MAAM,CAAC,CAKjB"}
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;AA4G/F;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoC9B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,MAAM,CAAC,CAKjB"}
@@ -8,7 +8,9 @@
8
8
  */
9
9
  import { basename } from "../../platform/compat/path/index.js";
10
10
  import { resolveImport } from "../../modules/import-map/resolver.js";
11
+ import { rendererLogger } from "../../utils/index.js";
11
12
  import { parseImports, replaceSpecifiers } from "./lexer.js";
13
+ const logger = rendererLogger.component("specifier-resolver");
12
14
  import { isCanonicalReactEsmUrl, isExternalScheme, isHttpUrl, isInternalBare, isParentHttpModule, isRelative, resolveBareSpecifier, } from "./http-cache-helpers.js";
13
15
  function isLocalMappedSpecifier(specifier) {
14
16
  return specifier.startsWith("/_vf_modules/") ||
@@ -80,20 +82,68 @@ async function resolveSpecifier(specifier, baseUrl, options, cacheHttpModule) {
80
82
  return mapped;
81
83
  return resolveSpecifier(mapped, baseUrl, options, cacheHttpModule);
82
84
  }
85
+ /**
86
+ * Specifiers this module only ever reaches through `import(...)`.
87
+ *
88
+ * The distinction decides what a resolution failure means. A static import is
89
+ * part of the emitted module's own import graph, so the artifact contract holds
90
+ * for it: every static dependency resolves to a local path before the module is
91
+ * handed to the runtime loader, and a failure to do that is fatal, exactly as
92
+ * it was before graceful degradation existed.
93
+ *
94
+ * A dynamic specifier is resolved by the runtime at call time and is routinely
95
+ * guarded by the caller (`platform/adapters/redis/modules.js` only calls
96
+ * `await import("redis")` when the redis adapter is actually used). Pre-fetching
97
+ * it is an optimisation, so failing to pre-fetch it leaves the specifier in
98
+ * place rather than taking down a render that would never have imported it.
99
+ */
100
+ function isDynamicOnly(imports) {
101
+ const dynamic = new Set();
102
+ const staticSpecifiers = new Set();
103
+ for (const imp of imports) {
104
+ if (!imp.n)
105
+ continue;
106
+ (imp.d > -1 ? dynamic : staticSpecifiers).add(imp.n);
107
+ }
108
+ for (const specifier of staticSpecifiers)
109
+ dynamic.delete(specifier);
110
+ return dynamic;
111
+ }
83
112
  /**
84
113
  * Build a map of specifier replacements by resolving all imports in the code.
114
+ *
115
+ * Resolution failure is fatal for a static import and best-effort for a
116
+ * specifier only ever used in `import(...)`. See {@link isDynamicOnly}.
85
117
  */
86
118
  export async function buildReplacements(code, baseUrl, options, cacheHttpModule) {
87
119
  const imports = await parseImports(code);
88
120
  const uniqueSpecifiers = [...new Set(imports.map((imp) => imp.n).filter(Boolean))];
89
- const results = await Promise.all(uniqueSpecifiers.map(async (specifier) => ({
121
+ const dynamicOnly = isDynamicOnly(imports);
122
+ const settled = await Promise.allSettled(uniqueSpecifiers.map(async (specifier) => ({
90
123
  specifier,
91
124
  resolved: await resolveSpecifier(specifier, baseUrl, options, cacheHttpModule),
92
125
  })));
93
126
  const replacements = new Map();
94
- for (const { specifier, resolved } of results) {
95
- if (resolved && resolved !== specifier)
96
- replacements.set(specifier, resolved);
127
+ for (let i = 0; i < settled.length; i++) {
128
+ const outcome = settled[i];
129
+ const specifier = uniqueSpecifiers[i];
130
+ if (!outcome || specifier === undefined)
131
+ continue;
132
+ if (outcome.status === "fulfilled") {
133
+ const { specifier: resolvedFor, resolved } = outcome.value;
134
+ if (resolved && resolved !== resolvedFor)
135
+ replacements.set(resolvedFor, resolved);
136
+ continue;
137
+ }
138
+ // A static import must resolve. Leaving one unresolved would emit a module
139
+ // whose own import graph reaches outside the local cache, which is not what
140
+ // the runtime loader is handed anywhere else.
141
+ if (!dynamicOnly.has(specifier))
142
+ throw outcome.reason;
143
+ logger.warn("Leaving an unresolvable dynamic specifier for runtime resolution", {
144
+ specifier,
145
+ error: outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason),
146
+ });
97
147
  }
98
148
  return replacements;
99
149
  }
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.1095";
2
+ export declare const VERSION = "0.1.1098";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.1095";
4
+ export const VERSION = "0.1.1098";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.1095",
3
+ "version": "0.1.1098",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -328,10 +328,10 @@
328
328
  "@types/react": "19.2.14",
329
329
  "@types/react-dom": "19.2.3",
330
330
  "ws": "8.21.0",
331
- "@veryfront/ext-bundler-esbuild": "0.1.1095",
332
- "@veryfront/ext-content-mdx": "0.1.1095",
333
- "@veryfront/ext-css-tailwind": "0.1.1095",
334
- "@veryfront/ext-parser-babel": "0.1.1095"
331
+ "@veryfront/ext-bundler-esbuild": "0.1.1098",
332
+ "@veryfront/ext-content-mdx": "0.1.1098",
333
+ "@veryfront/ext-css-tailwind": "0.1.1098",
334
+ "@veryfront/ext-parser-babel": "0.1.1098"
335
335
  },
336
336
  "devDependencies": {
337
337
  "@types/node": "20.9.0"