@webjsdev/server 0.8.76 → 0.8.77
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/package.json +1 -1
- package/src/component-elision.js +63 -0
- package/src/dev/handler.js +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/server",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.77",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The server for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Provides the file-based router, SSR, server actions, route handlers, middleware, and live reload on Node 24+ or Bun.",
|
|
6
6
|
"main": "index.js",
|
package/src/component-elision.js
CHANGED
|
@@ -1607,3 +1607,66 @@ export function lazyComponentFiles(components, elidable) {
|
|
|
1607
1607
|
}
|
|
1608
1608
|
return out;
|
|
1609
1609
|
}
|
|
1610
|
+
|
|
1611
|
+
/** `import <bindings> from 'x'` (the bindings part is captured). */
|
|
1612
|
+
const BINDING_IMPORT_RE = /\bimport\s+(type\s+)?([\w$*{}\s,]+?)\s+from\s+(['"])([^'"]+)\3/g;
|
|
1613
|
+
/** `export { a } from 'x'` / `export * from 'x'` (a re-export needs the module too). */
|
|
1614
|
+
const REEXPORT_RE = /\bexport\s+(type\s+)?(?:\*|\{[^}]*\}|\*\s+as\s+[\w$]+)\s+from\s+(['"])([^'"]+)\2/g;
|
|
1615
|
+
|
|
1616
|
+
/**
|
|
1617
|
+
* Is this import clause types only (`import type { A }`, or every named
|
|
1618
|
+
* specifier marked `type`)? Those are erased before the browser sees them.
|
|
1619
|
+
*
|
|
1620
|
+
* @param {string | undefined} typeKw
|
|
1621
|
+
* @param {string} clause
|
|
1622
|
+
*/
|
|
1623
|
+
function typeOnlyClause(typeKw, clause) {
|
|
1624
|
+
if (typeKw) return true;
|
|
1625
|
+
const m = clause.trim().match(/^\{([\s\S]*)\}$/);
|
|
1626
|
+
if (!m) return false;
|
|
1627
|
+
const names = m[1].split(',').map((s) => s.trim()).filter(Boolean);
|
|
1628
|
+
return names.length > 0 && names.every((n) => /^type\s/.test(n));
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
/**
|
|
1632
|
+
* The lazy component files some module imports WITH bindings (#1524). A
|
|
1633
|
+
* binding import loads the module eagerly whatever the class says, because the
|
|
1634
|
+
* importer needs the value when it runs. Such a file is not lazy in practice,
|
|
1635
|
+
* so the caller drops it from the lazy set: otherwise the preload walks would
|
|
1636
|
+
* skip a module the browser still fetches up front, which only makes it slower.
|
|
1637
|
+
*
|
|
1638
|
+
* Only importers whose graph edges reach a lazy file are read. A server file is
|
|
1639
|
+
* never served, so its imports do not count.
|
|
1640
|
+
*
|
|
1641
|
+
* @param {Map<string, string[]>} lazyFiles
|
|
1642
|
+
* @param {import('./module-graph.js').ModuleGraph | undefined} moduleGraph
|
|
1643
|
+
* @param {(file: string) => Promise<string>} readFileFn
|
|
1644
|
+
* @param {(spec: string, fromFile: string, appDir: string) => (string|null)} resolveImport
|
|
1645
|
+
* @param {string} appDir
|
|
1646
|
+
* @returns {Promise<Set<string>>}
|
|
1647
|
+
*/
|
|
1648
|
+
export async function bindingImportedLazyFiles(lazyFiles, moduleGraph, readFileFn, resolveImport, appDir) {
|
|
1649
|
+
/** @type {Set<string>} */
|
|
1650
|
+
const out = new Set();
|
|
1651
|
+
if (!lazyFiles.size || !moduleGraph) return out;
|
|
1652
|
+
for (const [importer, deps] of moduleGraph) {
|
|
1653
|
+
if (SERVER_FILE_RE.test(importer)) continue;
|
|
1654
|
+
let reaches = false;
|
|
1655
|
+
for (const d of deps) if (lazyFiles.has(d) && !out.has(d)) { reaches = true; break; }
|
|
1656
|
+
if (!reaches) continue;
|
|
1657
|
+
let src;
|
|
1658
|
+
try { src = await readFileFn(importer); } catch { continue; }
|
|
1659
|
+
const code = redactStringsAndTemplates(maskComments(src));
|
|
1660
|
+
for (const m of code.matchAll(BINDING_IMPORT_RE)) {
|
|
1661
|
+
if (typeOnlyClause(m[1], m[2])) continue;
|
|
1662
|
+
const abs = resolveImport(m[4], importer, appDir);
|
|
1663
|
+
if (abs && lazyFiles.has(abs)) out.add(abs);
|
|
1664
|
+
}
|
|
1665
|
+
for (const m of code.matchAll(REEXPORT_RE)) {
|
|
1666
|
+
if (m[1]) continue;
|
|
1667
|
+
const abs = resolveImport(m[3], importer, appDir);
|
|
1668
|
+
if (abs && lazyFiles.has(abs)) out.add(abs);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
return out;
|
|
1672
|
+
}
|
package/src/dev/handler.js
CHANGED
|
@@ -31,9 +31,9 @@ import { mintNonce, buildCspHeader, cspHeaderName } from '../csp.js';
|
|
|
31
31
|
import { propagateTrustedRemoteIp } from '../rate-limit.js';
|
|
32
32
|
import { reachedBareImports, resolveVendorImports, clearVendorCache, hasVendorPin, readPinFile, prunePinToReachable } from '../vendor.js';
|
|
33
33
|
import { browserEntryFiles } from '../browser-entries.js';
|
|
34
|
-
import { buildModuleGraph, seenFilesFor, appImportsMap, reachableFromEntries } from '../module-graph.js';
|
|
34
|
+
import { buildModuleGraph, seenFilesFor, appImportsMap, reachableFromEntries, resolveImport } from '../module-graph.js';
|
|
35
35
|
import { primeComponentRegistry, findOrphanComponents, scanComponents } from '../component-scanner.js';
|
|
36
|
-
import { analyzeElision, lazyComponentFiles } from '../component-elision.js';
|
|
36
|
+
import { analyzeElision, lazyComponentFiles, bindingImportedLazyFiles } from '../component-elision.js';
|
|
37
37
|
|
|
38
38
|
import { setVendorEntries, setCoreInstall, publishBuildId, setAppSourceId, setBasePath, basePath, setImportAliasEntries, importAliasBrowserEntries } from '../importmap.js';
|
|
39
39
|
import { stripBasePath, withBasePath } from '../base-path.js';
|
|
@@ -614,6 +614,10 @@ export async function createRequestHandler(opts) {
|
|
|
614
614
|
state.inertRouteModules = r.inertRouteModules;
|
|
615
615
|
state.importOnlyRouteModules = r.importOnlyRouteModules;
|
|
616
616
|
state.lazyComponentFiles = lazyComponentFiles(components, state.elidableComponents);
|
|
617
|
+
// A lazy component some module imports WITH bindings loads eagerly
|
|
618
|
+
// anyway, so it keeps its preload hint and its imports as written.
|
|
619
|
+
for (const f of await bindingImportedLazyFiles(state.lazyComponentFiles, state.moduleGraph,
|
|
620
|
+
(f) => readFile(f, 'utf8'), resolveImport, appDir)) state.lazyComponentFiles.delete(f);
|
|
617
621
|
// Dev live-reload classification (#1398): derive the three sets the
|
|
618
622
|
// watch-event classifier reads. `browserBoundFiles` above is the
|
|
619
623
|
// AUTHORIZATION gate (it walks from every entry, elided ones
|