importClassifier
uses node:path
import { externalModule, isServerHostFile, SERVER_API, SERVER_HOST, TCM_REACT_GONE, TCM_SERVER_GONE, } from "#codebase/tcmts/core/analyze"; import type { AnalyzeHost } from "#codebase/tcmts/core/analyze"; import type { Resolved } from "#codebase/tcmts/core/canonicalize"; import { TcmError } from "#codebase/tcmts/core/extract"; import { hashFromExportId } from "#codebase/tcmts/core/hash"; import { REACT_MODULE, SERVER_HOST_MODULE } from "#codebase/tcmts/core/types"; import path_2 from "node:path"; import { isImportClause as ts_isImportClause, isImportSpecifier as ts_isImportSpecifier } from "typescript/ast"; import type { ImportClause as ts_ImportClause, ImportDeclaration as ts_ImportDeclaration, ImportSpecifier as ts_ImportSpecifier, NamespaceImport as ts_NamespaceImport, Node as ts_Node, StringLiteral as ts_StringLiteral, } from "typescript/ast"; export function importClassifier(file: string, host: AnalyzeHost) { const cache = new Map<string, Resolved>(); const ids = new Map<ts_Node, number>(); return ( d: ts_ImportSpecifier | ts_ImportClause | ts_NamespaceImport, meaning: "type" | "value", ): Resolved => { if (!ids.has(d)) ids.set(d, ids.size); const key = `${ids.get(d)} ${meaning}`; const hit = cache.get(key); if (hit) return hit; const decl = ts_isImportSpecifier(d) ? d.parent.parent.parent : ts_isImportClause(d) ? d.parent : d.parent.parent; const spec = ((decl as ts_ImportDeclaration).moduleSpecifier as ts_StringLiteral).text; const exportName = ts_isImportSpecifier(d) ? (d.propertyName ?? d.name).text : ts_isImportClause(d) ? "default" : "*"; let r: Resolved; if (spec === SERVER_API) { throw new TcmError(TCM_SERVER_GONE, d); } else if (spec === SERVER_HOST || isServerHostFile(spec, file, host)) { r = { kind: "ext", ext: { module: SERVER_HOST_MODULE, subpath: "", export: exportName } }; } else if (spec === "#tcm/react") { throw new TcmError(TCM_REACT_GONE, d); } else if ( /^\.\.?\//.test(spec) && path_2.resolve(path_2.dirname(file), spec) === path_2.join(host.cacheDir, "tcm-cache.js") ) { // React's cache and cacheSignal, as a cache module imports them (tcm's: see `loader.render`): React's, as written r = { kind: "ext", ext: { module: REACT_MODULE, subpath: "", export: exportName } }; } else if (spec === "#codebase" || spec.startsWith("#codebase/")) { if (exportName === "*" || exportName === "default") { throw new TcmError( 'import codebase definitions by name: `import { sort } from "#codebase/lib"`', d, ); } const ns = spec.slice("#codebase".length).replace(/^\//, "").replaceAll("/", "."); try { r = { kind: "hash", hash: host.resolveName(ns ? `${ns}.${exportName}` : exportName, meaning), }; } catch (e) { throw new TcmError((e as Error).message, d); } } else if (spec.startsWith(".") || spec.startsWith("/")) { const abs = path_2.resolve(path_2.dirname(file), spec); const hash = hashFromExportId(exportName); if (path_2.dirname(abs) !== host.cacheDir || !hash) { throw new TcmError( `cannot import local file '${spec}': add it to the codebase and import it from '#codebase/...'`, d, ); } r = { kind: "hash", hash }; } else { r = { kind: "ext", ext: externalModule(spec, file, d, exportName, host) }; } cache.set(key, r); return r; }; }
uses
- tcmts.core.analyze.AnalyzeHost
- tcmts.core.analyze.SERVER_API
- tcmts.core.analyze.SERVER_HOST
- tcmts.core.analyze.TCM_REACT_GONE
- tcmts.core.analyze.TCM_SERVER_GONE
- tcmts.core.analyze.externalModule
- tcmts.core.analyze.isServerHostFile
- tcmts.core.canonicalize.Resolved
- tcmts.core.extract.TcmError
- tcmts.core.hash.hashFromExportId
- tcmts.core.types.REACT_MODULE
- tcmts.core.types.SERVER_HOST_MODULE