UNPKG

@embeddable.com/sdk-core

Version:

Core Embeddable SDK module responsible for web-components bundling and publishing.

220 lines (186 loc) 6.09 kB
/** * Most of the code in this file is copied from the following file: * https://github.com/swc-project/swc-node/blob/master/packages/register/esm.mts * * The main difference is that resolve function is modified to handle non-TS files. * * */ import { fileURLToPath, pathToFileURL } from "url"; import { dirname, join } from "path"; import ts from "typescript"; import { readDefaultTsConfig } from "@swc-node/register/read-default-tsconfig"; import { compile } from "./custom-esm-register.cjs"; import fs from "fs"; import path from "path"; // Find the tsconfig.json in the customer's project directory const tsconfigPath = ts.findConfigFile( process.cwd(), fs.existsSync, "tsconfig.json", ); let tsconfig; if (tsconfigPath) { // Read and parse the tsconfig.json const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile); const parsedConfig = ts.parseJsonConfigFileContent( configFile.config, ts.sys, dirname(tsconfigPath), ); tsconfig = parsedConfig.options; } else { tsconfig = await readDefaultTsConfig(); } // Set necessary compiler options tsconfig.module = ts.ModuleKind.ESNext; tsconfig.moduleResolution = ts.ModuleResolutionKind.Node16; const moduleResolutionCache = ts.createModuleResolutionCache( ts.sys.getCurrentDirectory(), (x) => x, tsconfig, ); const host = { fileExists: ts.sys.fileExists, readFile: ts.sys.readFile, }; const EXTENSIONS = [ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Mts]; const NON_JS_TS_EXTENSIONS = /\.(css|scss|less|sass|styl|json|svg|woff|woff2|png|jpg|jpeg|gif|webp|md|yml|yaml)$/; const currentDir = dirname(fileURLToPath(import.meta.url)); const isWindows = process.platform === "win32"; function cleanWindowsFileURL(url) { // Remove any duplicate file:/ prefixes and normalize slashes return url .replace(/file:\/+/g, "file:///") .replace(/\\/g, "/") .replace(/file:\/+([A-Za-z]:)/, "file:///$1"); } export const resolve = async (specifier, context, nextResolve) => { if (NON_JS_TS_EXTENSIONS.test(specifier)) { const mockModulePath = pathToFileURL( join(currentDir, "mock-module.js"), ).href; return { format: "module", url: mockModulePath, shortCircuit: true, }; } const isTS = EXTENSIONS.some((ext) => specifier.endsWith(ext)); // Entry point if (!context.parentURL) { if (isWindows) { // Handle the case where the specifier contains both the cwd and the actual path const parts = specifier.split("file:/"); const lastPart = parts[parts.length - 1]; // Convert the path to a proper file URL const cleanPath = lastPart.replace(/^\/+/, ""); const entryPointPath = pathToFileURL(cleanPath).href; return { format: isTS ? "ts" : undefined, url: cleanWindowsFileURL(entryPointPath), shortCircuit: true, }; } return { format: isTS ? "ts" : undefined, url: specifier, shortCircuit: true, }; } // Handle SDK relative imports if ( isWindows && specifier.startsWith(".") && context.parentURL?.includes("embeddable-sdk") ) { const parentPath = fileURLToPath(context.parentURL); const parentDir = dirname(parentPath); let resolvedPath = join(parentDir, specifier); // If no extension is provided, try common extensions if (!path.extname(resolvedPath)) { const extensions = [".js", ".mjs", ".cjs", ".ts", ".mts"]; for (const ext of extensions) { const pathWithExt = resolvedPath + ext; if (fs.existsSync(pathWithExt) && fs.statSync(pathWithExt).isFile()) { resolvedPath = pathWithExt; break; } } } // Check if the resolved path exists and is a file if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) { return { url: pathToFileURL(resolvedPath).href, shortCircuit: true, }; } } // Determine if this is a package import const isPackageImport = !specifier.startsWith(".") && !specifier.startsWith("/") && !/^[A-Z]:/.test(specifier); // Windows path // External library import if ( (context.parentURL?.includes("/node_modules/") || isPackageImport) && !isTS ) { return nextResolve(specifier); } const isFileUrl = specifier.startsWith("file://"); const isParentFileUrl = context.parentURL?.startsWith("file://"); // Resolve the module using TypeScript's module resolution const { resolvedModule } = ts.resolveModuleName( isFileUrl ? fileURLToPath(specifier) : specifier, isParentFileUrl ? fileURLToPath(context.parentURL) : context.parentURL, tsconfig, host, moduleResolutionCache, ); // Local project TS file if ( resolvedModule && !resolvedModule.resolvedFileName.includes("/node_modules/") && EXTENSIONS.includes(resolvedModule.extension) ) { const resolved = { format: "ts", url: pathToFileURL(resolvedModule.resolvedFileName).href + `?update=${Date.now()}`, shortCircuit: true, }; return resolved; } // Fallback for non-TS files or unresolved modules const specifierPathOrUrl = !resolvedModule?.resolvedFileName?.includes("/node_modules/") && isWindows ? pathToFileURL(specifier).href : specifier; return nextResolve(specifierPathOrUrl); }; export const load = async (url, context, nextLoad) => { if (context.format === "ts") { const { source } = await nextLoad(url, context); const code = typeof source === "string" ? source : Buffer.from(source).toString(); // Compile the code using @swc-node with the customer's tsconfig const compiled = await compile(code, fileURLToPath(url), tsconfig, true); const resolved = { format: "module", source: compiled, shortCircuit: true, }; return resolved; } else { if ( isWindows && !url.startsWith("file://") && url.includes("node_modules") ) { return nextLoad(pathToFileURL(url).href, context); } return nextLoad(url, context); } };