UNPKG

@storm-stack/core

Version:

A build toolkit and runtime used by Storm Software in TypeScript applications

283 lines (274 loc) 14 kB
import { getFileHeader } from './chunk-YZLXVHOH.js'; import { ModuleResolverPlugin } from './chunk-6J7JLHUX.js'; import { writeMetaFile } from './chunk-22M3FAZZ.js'; import { writeFile } from './chunk-2LIG4LH7.js'; import { getSourceFile, getString } from './chunk-K4ZS2URJ.js'; import { getParsedTypeScriptConfig } from './chunk-ONS37BLK.js'; import { __name } from './chunk-43IZMM3W.js'; import { LogLevelLabel } from '@storm-software/config-tools/types'; import { createDirectory } from '@stryke/fs/helpers'; import { existsSync } from '@stryke/path/exists'; import { joinPaths } from '@stryke/path/join-paths'; import { findFileExtension, relativePath } from '@stryke/path/file-path-fns'; import { isParentPath } from '@stryke/path/is-parent-path'; import { replacePath } from '@stryke/path/replace'; import { Extractor, ExtractorConfig } from '@microsoft/api-extractor'; import { resolvePackage } from '@stryke/path/resolve'; import defu from 'defu'; import { createProgram, createCompilerHost, getPreEmitDiagnostics, getLineAndCharacterOfPosition, flattenDiagnosticMessageText } from 'typescript'; async function prepareConfig(context, hooks) { context.log(LogLevelLabel.TRACE, `Preparing the configuration for the Storm Stack project.`); await hooks.callHook("prepare:config", context).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occurred while preparing the configuration for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occurred while preparing the configuration for the Storm Stack project", { cause: error }); }); } __name(prepareConfig, "prepareConfig"); async function prepareDeploy(context, hooks) { context.log(LogLevelLabel.TRACE, `Initializing the deployment configuration for the Storm Stack project.`); await hooks.callHook("prepare:deploy", context).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occurred while initializing the deployment configuration for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occurred while initializing the deployment configuration for the Storm Stack project", { cause: error }); }); } __name(prepareDeploy, "prepareDeploy"); async function prepareEntry(context, hooks) { context.log(LogLevelLabel.TRACE, `Initializing the reflection data for the Storm Stack project.`); await hooks.callHook("prepare:entry", context).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occurred while initializing the reflection data for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occurred while initializing the reflection data for the Storm Stack project", { cause: error }); }); } __name(prepareEntry, "prepareEntry"); async function prepareRuntime(context, hooks) { context.log(LogLevelLabel.TRACE, `Preparing the runtime artifacts for the Storm Stack project.`); await context.vfs.rm(context.runtimePath); await hooks.callHook("prepare:runtime", context).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occurred while preparing the runtime artifacts for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occurred while preparing the runtime artifacts for the Storm Stack project", { cause: error }); }); context.log(LogLevelLabel.TRACE, "Generating runtime barrel file"); await context.vfs.writeRuntimeFile("index", joinPaths(context.runtimePath, "index.ts"), ` ${getFileHeader()} ${(await context.vfs.listRuntimeFiles()).filter((file) => !isParentPath(file.path, joinPaths(context.runtimePath, "log")) && !isParentPath(file.path, joinPaths(context.runtimePath, "storage"))).map((file) => `export * from "./${replacePath(file.path, context.runtimePath).replace(findFileExtension(file.path), "")}";`).join("\n")} `); } __name(prepareRuntime, "prepareRuntime"); async function prepareTypes(context, hooks) { context.log(LogLevelLabel.TRACE, `Preparing the TypeScript definitions for the Storm Stack project.`); await context.vfs.rm(context.runtimeDtsFilePath); context.log(LogLevelLabel.TRACE, "Transforming runtime files."); const runtimeFiles = await Promise.all((await context.vfs.listRuntimeFiles()).filter((file) => !context.vfs.isMatchingRuntimeId("index", file.id)).map(async (file) => { file.contents = await context.compiler.transform(context, file.path, file.contents, { skipTransformUnimport: true, babel: { plugins: [ ModuleResolverPlugin, ...context.options.babel.plugins ] } }); context.log(LogLevelLabel.TRACE, `Writing transformed runtime file ${file.id}.`); await context.vfs.writeRuntimeFile(file.id, file.path, file.contents); return file.path; })); const typescriptPath = await resolvePackage("typescript"); if (!typescriptPath) { throw new Error("Could not resolve TypeScript package location. Please ensure TypeScript is installed."); } const files = runtimeFiles.reduce( (ret, fileName) => { const formatted = replacePath(fileName, context.options.workspaceRoot); if (!ret.includes(formatted)) { ret.push(formatted); } return ret; }, [ joinPaths(typescriptPath, "lib", "lib.esnext.full.d.ts") ] // await listFiles(joinPaths(typescriptPath, "lib", "lib.*.d.ts")) ); context.log(LogLevelLabel.TRACE, "Parsing TypeScript configuration for the Storm Stack project."); const sourceFileDts = getSourceFile(context.runtimeDtsFilePath, `${getFileHeader(null, false)} `); await hooks.callHook("prepare:types", context, sourceFileDts).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occurred while preparing the TypeScript definitions for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occurred while preparing the TypeScript definitions for the Storm Stack project", { cause: error }); }); await context.vfs.writeFileToDisk(sourceFileDts.id, getString(sourceFileDts.code)); const resolvedTsconfig = getParsedTypeScriptConfig(context.options.workspaceRoot, context.options.projectRoot, context.tsconfig.tsconfigFilePath, defu({ compilerOptions: { strict: false, noEmit: false, declaration: true, declarationMap: false, emitDeclarationOnly: true, skipLibCheck: true }, exclude: [ "node_modules", "dist" ], include: files }, context.options.tsconfigRaw ?? {})); resolvedTsconfig.options.configFilePath = joinPaths(context.options.workspaceRoot, context.tsconfig.tsconfigFilePath); resolvedTsconfig.options.pathsBasePath = context.options.workspaceRoot; resolvedTsconfig.options.suppressOutputPathCheck = true; context.log(LogLevelLabel.TRACE, "Creating the TypeScript compiler host"); const program = createProgram(files, resolvedTsconfig.options, createCompilerHost(resolvedTsconfig.options)); context.log(LogLevelLabel.TRACE, `Running TypeScript compiler on ${runtimeFiles.length} runtime files.`); let runtimeModules = ""; const emitResult = program.emit(void 0, (fileName, text, _, __, sourceFiles, _data) => { const sourceFile2 = sourceFiles?.[0]; if (sourceFile2?.fileName && !fileName.endsWith(".map")) { if (context.vfs.isRuntimeFile(sourceFile2.fileName)) { runtimeModules += ` declare module "${context.vfs.resolveId(sourceFile2.fileName)}" { ${text.trim().replace(/^\s*export\s*declare\s*/gm, "export ").replace(/^\s*declare\s*/gm, "")} } `; } } }, void 0, true); const diagnostics = getPreEmitDiagnostics(program).concat(emitResult.diagnostics); const diagnosticMessages = []; diagnostics.forEach((diagnostic) => { if (diagnostic.file) { const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); const message = flattenDiagnosticMessageText(diagnostic.messageText, "\n"); diagnosticMessages.push(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); } else { const message = flattenDiagnosticMessageText(diagnostic.messageText, "\n"); diagnosticMessages.push(message); } }); const diagnosticMessage = diagnosticMessages.join("\n"); if (diagnosticMessage) { throw new Error(`TypeScript compilation failed: ${diagnosticMessage.length > 5e3 ? `${diagnosticMessage.slice(0, 5e3)}...` : diagnosticMessage}`); } const mainEntryPointFilePath = process.env.STORM_STACK_LOCAL ? joinPaths(context.options.workspaceRoot, "dist/packages/types") : await resolvePackage("@storm-stack/types"); if (!mainEntryPointFilePath) { throw new Error("Could not resolve @storm-stack/types package location."); } context.log(LogLevelLabel.TRACE, `Running API Extractor on the @storm-stack/types package at ${mainEntryPointFilePath}.`); const untrimmedFilePath = joinPaths(context.dtsPath, `${context.meta.projectRootHash}.d.ts`); const extractorResult = Extractor.invoke(ExtractorConfig.prepare({ configObject: { mainEntryPointFilePath: joinPaths(mainEntryPointFilePath, "dist/esm/src/index.d.ts"), apiReport: { enabled: false, // `reportFileName` is not been used. It's just to fit the requirement of API Extractor. reportFileName: "report.api.md" }, docModel: { enabled: false }, dtsRollup: { enabled: true, untrimmedFilePath }, tsdocMetadata: { enabled: false }, compiler: { tsconfigFilePath: relativePath(joinPaths(context.options.workspaceRoot, context.options.projectRoot), joinPaths(context.options.workspaceRoot, context.tsconfig.tsconfigFilePath)) }, projectFolder: joinPaths(context.options.workspaceRoot, context.options.projectRoot), newlineKind: "lf" }, configObjectFullPath: void 0, packageJsonFullPath: joinPaths(context.options.workspaceRoot, context.options.projectRoot, "package.json") }), { localBuild: true, showVerboseMessages: true }); if (!extractorResult.succeeded) { throw new Error(`API Extractor completed with ${extractorResult.errorCount} errors and ${extractorResult.warningCount} warnings when processing @storm-stack/types package.`); } context.log(LogLevelLabel.TRACE, `Generating TypeScript declaration file in ${context.runtimeDtsFilePath}.`); const sourceFile = getSourceFile(context.runtimeDtsFilePath, `${getFileHeader(null, false)} // This file is an augmentation to the built-in StormContext interface // Thus cannot contain any top-level imports // <https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation> ${(await context.vfs.readFile(untrimmedFilePath)).replace(/\s*export.*__Ω.*;/g, "").replace(/^export\s*\{\s*\}\s*$/gm, "").replace(/^export\s*(?:declare\s*)?interface\s*/gm, "interface ").replace(/^export\s*(?:declare\s*)?type\s*/gm, "type ").replace(/^export\s*(?:declare\s*)?const\s*/gm, "declare const ").replace(/: Storage(?:_\d+)?;$/gm, ': import("unstorage").Storage<import("unstorage").StorageValue>;')} ${runtimeModules}`.replace( // eslint-disable-next-line regexp/no-super-linear-backtracking /import\s*(?:type\s*)?\{?[\w,\s]*(?:\}\s*)?from\s*(?:'|")@?[a-zA-Z0-9-\\/.]*(?:'|");?/g, "" ).replaceAll("#private;", "")); await hooks.callHook("prepare:types", context, sourceFile).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occurred while preparing the TypeScript definitions for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occurred while preparing the TypeScript definitions for the Storm Stack project", { cause: error }); }); await context.vfs.writeFileToDisk(sourceFile.id, getString(sourceFile.code)); } __name(prepareTypes, "prepareTypes"); // src/commands/prepare/index.ts async function prepare(context, hooks) { await writeFile(context.log, joinPaths(context.dataPath, "meta.json"), JSON.stringify({ ...context.meta, runtimeIdMap: JSON.stringify(context.meta.runtimeIdMap), virtualFiles: JSON.stringify(context.meta.virtualFiles) }, null, 2)); context.persistedMeta = context.meta; await hooks.callHook("prepare:begin", context).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occured while starting the prepare process for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occured while starting the prepare process for the Storm Stack project", { cause: error }); }); if (!existsSync(context.cachePath)) { await createDirectory(context.cachePath); } if (!existsSync(context.dataPath)) { await createDirectory(context.dataPath); } await prepareConfig(context, hooks); if (context.options.projectType === "application") { await prepareRuntime(context, hooks); } if (context.options.output.dts !== false) { await prepareTypes(context, hooks); } if (context.options.projectType === "application") { await prepareEntry(context, hooks); } await prepareDeploy(context, hooks); context.tsconfig = getParsedTypeScriptConfig(context.options.workspaceRoot, context.options.projectRoot, context.options.tsconfig); if (!context.tsconfig) { throw new Error("Failed to parse the TypeScript configuration file."); } await hooks.callHook("prepare:complete", context).catch((error) => { context.log(LogLevelLabel.ERROR, `An error occured while finishing the prepare process for the Storm Stack project: ${error.message} ${error.stack ?? ""}`); throw new Error("An error occured while finishing the prepare process for the Storm Stack project", { cause: error }); }); await writeMetaFile(context); } __name(prepare, "prepare"); export { prepare }; //# sourceMappingURL=chunk-FN2K3AHM.js.map //# sourceMappingURL=chunk-FN2K3AHM.js.map