@storm-stack/core
Version:
A build toolkit and runtime used by Storm Software in TypeScript applications
283 lines (274 loc) • 14 kB
JavaScript
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