UNPKG

@esmx/core

Version:

A high-performance microfrontend framework supporting Vue, React, Preact, Solid, and Svelte with SSR and Module Linking capabilities.

86 lines (78 loc) 3.21 kB
import type fs from 'node:fs'; import fsp from 'node:fs/promises'; import path from 'node:path'; import type { ImportMap, SpecifierMap } from '@esmx/import'; import type { ParsedModuleConfig } from '../module-config'; import * as esmLexer from 'es-module-lexer'; /** * Get the list of statically imported module names from JS code. Maybe cannot handle multiple concurrent calls, not tested. * @param code JS code * @returns `Promise<string[]>` List of statically imported module names */ export async function getImportsFromJsCode(code: string): Promise<string[]> { await esmLexer.init; const [imports] = esmLexer.parse(code); // Static import && has module name return imports .filter((item) => item.t === 1 && item.n) .map((item) => item.n as string); } /** * Get the list of statically imported module names from a JS file. * @param filepath JS file path * @returns `Promise<string[]>` List of statically imported module names */ export async function getImportsFromJsFile( filepath: fs.PathLike | fs.promises.FileHandle ) { const source = await fsp.readFile(filepath, 'utf-8'); return getImportsFromJsCode(source); } export type ImportPreloadInfo = SpecifierMap; /** * Get import preload information. * @param specifier Module name * @param importMap Import map object * @param moduleConfig Module configuration * @returns * - `Promise<{ [specifier: string]: ImportPreloadPathString }>` Mapping object of module names to file paths * - `null` specifier does not exist */ export async function getImportPreloadInfo( specifier: string, importMap: ImportMap, moduleConfig: ParsedModuleConfig ) { const importInfo = importMap.imports; if (!importInfo || !(specifier in importInfo)) { return null; } const ans: ImportPreloadInfo = { // Entry file is also added to the preload list [specifier]: importInfo[specifier] }; // Lexical analysis is a time-consuming operation, so the fewer files processed, the faster, in other words, the shallower the depth, the faster, so breadth-first search is used here const needHandles: string[] = [specifier]; while (needHandles.length) { const specifier = needHandles.shift()!; let filepath = importInfo[specifier]; const splitRes = filepath.split('/'); if (splitRes[0] === '') splitRes.shift(); // Here it is assumed that the first directory in the path is the soft package name const name = splitRes.shift() + ''; const link = moduleConfig.links[name]; if (!link) { continue; } filepath = path.join(link.client, ...splitRes); const imports = await getImportsFromJsFile(filepath); for (const specifier of imports) { // If the module name does not exist in importMap or has been processed if (!(specifier in importInfo) || specifier in ans) continue; ans[specifier] = importInfo[specifier]; needHandles.push(specifier); } } // Reverse order, theoretically browser parsing may be faster after reversing return Object.fromEntries(Object.entries(ans).reverse()); }