@storm-stack/core
Version:
A build toolkit and runtime used by Storm Software in TypeScript applications
397 lines (388 loc) • 18.1 kB
JavaScript
import { installPackage } from './chunk-R43J2D55.js';
import { __VFS_INIT__ } from './chunk-NPDWYHER.js';
import { createUnimport } from './chunk-KRRKXJET.js';
import { writeFile } from './chunk-2LIG4LH7.js';
import { Compiler } from './chunk-V2KW5JFJ.js';
import { defaultEnvironmentName } from './chunk-EAAFBEI6.js';
import { getParsedTypeScriptConfig, getTsconfigFilePath, isIncludeMatchFound } from './chunk-ONS37BLK.js';
import { __name } from './chunk-43IZMM3W.js';
import { LogLevelLabel } from '@storm-software/config-tools/types';
import { parseTypeDefinition } from '@stryke/convert/parse-type-definition';
import { isSetString } from '@stryke/type-checks/is-set-string';
import { hash } from '@stryke/hash/hash';
import { joinPaths } from '@stryke/path/join-paths';
import { getObjectDiff } from '@donedeal0/superdiff';
import { readJsonFile } from '@stryke/fs/json';
import { isPackageExists } from '@stryke/fs/package-fns';
import { StormJSON } from '@stryke/json/storm-json';
import { titleCase } from '@stryke/string-format/title-case';
import chalk from 'chalk';
import { loadTsConfig } from '@stryke/fs/tsconfig';
import { relativePath, findFilePath, findFileName } from '@stryke/path/file-path-fns';
import ts from 'typescript';
function resolveEntry(options, entry) {
const parsed = parseTypeDefinition(entry);
const entryFile = parsed.file.replace(options.options.projectRoot, "").replaceAll("\\", "/").replaceAll(/^(?:\.\/)*/g, "");
return {
file: joinPaths(options.artifactsPath, `entry-${hash({
file: entryFile,
name: parsed.name
}, {
maxLength: 24
}).replaceAll("-", "0").replaceAll("_", "1")}.ts`),
name: parsed.name
};
}
__name(resolveEntry, "resolveEntry");
// src/commands/init/entry/index.ts
async function initEntry(context, hooks) {
context.log(LogLevelLabel.TRACE, `Initializing the entry points for the Storm Stack project.`);
if (context.options.projectType === "application" && context.options.entry) {
if (isSetString(context.options.entry)) {
context.entry = [
{
...resolveEntry(context, context.options.entry),
input: parseTypeDefinition(context.options.entry)
}
];
} else if (Array.isArray(context.options.entry) && context.options.entry.filter(Boolean).length > 0) {
context.entry = context.options.entry.map((entry) => ({
...resolveEntry(context, entry),
input: parseTypeDefinition(entry)
})).filter(Boolean);
}
}
await hooks.callHook("init:entry", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occurred while initializing the entry points for the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occurred while initializing the entry points for the Storm Stack project", {
cause: error
});
});
}
__name(initEntry, "initEntry");
// src/commands/init/install/node.ts
function getNodeDeps(context) {
context.packageDeps ??= {};
context.packageDeps["@types/node"] = "devDependency";
if (context.options.projectType === "application") {
context.packageDeps.unctx = "dependency";
}
return context.packageDeps;
}
__name(getNodeDeps, "getNodeDeps");
// src/commands/init/install/shared.ts
function getSharedDeps(context) {
context.packageDeps ??= {};
context.packageDeps["@storm-stack/types"] = "devDependency";
if (context.options.projectType === "application") {
context.packageDeps.unstorage = "dependency";
}
return context.packageDeps;
}
__name(getSharedDeps, "getSharedDeps");
// src/commands/init/install/index.ts
async function initInstall(context, hooks) {
context.log(LogLevelLabel.TRACE, `Checking and installing missing project dependencies.`);
context.packageDeps = getSharedDeps(context);
if (context.options.platform === "node") {
const installs = getNodeDeps(context);
context.packageDeps = Object.keys(installs).reduce((ret, key) => {
if (installs[key] && ret[key] !== "dependency") {
ret[key] = installs[key];
}
return ret;
}, context.packageDeps);
}
await hooks.callHook("init:install", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occured while installing project dependencies: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occured while installing project dependencies", {
cause: error
});
});
context.log(LogLevelLabel.TRACE, `The following packages must be installed as dependencies:
${Object.keys(context.packageDeps).map((key) => ` - ${key} (${context.packageDeps[key]})`).join("\n")}`);
for (const [key, value] of Object.entries(context.packageDeps)) {
await installPackage(context, key, value === "devDependency");
}
}
__name(initInstall, "initInstall");
async function initOptions(context, hooks) {
context.log(LogLevelLabel.TRACE, `Initializing the processing options for the Storm Stack project.`);
if (context.packageJson) {
if (context.options.command === "new") {
context.options.repository ??= typeof context.packageJson.repository === "string" ? context.packageJson.repository : context.packageJson.repository?.url;
} else {
if (context.packageJson?.name) {
context.options.name ??= context.packageJson?.name;
}
context.options.description ??= context.packageJson?.description;
context.options.repository ??= typeof context.packageJson?.repository === "string" ? context.packageJson.repository : context.packageJson?.repository?.url;
}
} else if (context.options.command !== "new") {
throw new Error(`The package.json file is missing in the project root directory: ${context.options.projectRoot}. Please run the "new" command to create a new Storm Stack project.`);
}
if (context.projectJson) {
context.options.projectType ??= context.projectJson.projectType;
context.options.name ??= context.projectJson.name;
if (context.options.name?.startsWith("@") && context.options.name.split("/").filter(Boolean).length > 1) {
context.options.name = context.options.name.split("/").filter(Boolean)[1];
}
}
context.options.tsconfig ??= joinPaths(context.options.projectRoot, "tsconfig.json");
context.options.esbuild.override ??= {};
context.options.external ??= [];
context.options.noExternal ??= [];
context.options.esbuild.target ??= "esnext";
context.options.esbuild.format ??= "esm";
context.options.environment ??= defaultEnvironmentName(context.options);
await hooks.callHook("init:options", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occurred while initializing the options for the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occurred while initializing the options for the Storm Stack project", {
cause: error
});
});
context.log(LogLevelLabel.TRACE, "Initialized the processing options for the Storm Stack project.");
}
__name(initOptions, "initOptions");
async function initReflections(context, hooks) {
context.log(LogLevelLabel.TRACE, "Initializing the reflections for the Storm Stack project.");
await hooks.callHook("init:reflections", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occurred while initializing the reflections for the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occurred while initializing the reflections for the Storm Stack project", {
cause: error
});
});
context.log(LogLevelLabel.TRACE, "Initialized the reflections for the Storm Stack project.");
}
__name(initReflections, "initReflections");
async function getTsconfigChanges(context) {
const tsconfig = getParsedTypeScriptConfig(context.options.workspaceRoot, context.options.projectRoot, context.options.tsconfig, context.options.tsconfigRaw);
const tsconfigFilePath = getTsconfigFilePath(context.options.projectRoot, context.options.tsconfig);
const tsconfigJson = await readJsonFile(tsconfigFilePath);
tsconfigJson.compilerOptions ??= {};
const extendedTsconfig = await loadTsConfig(tsconfigFilePath);
extendedTsconfig.compilerOptions ??= {};
if (tsconfigJson.reflection !== true) {
tsconfigJson.reflection = true;
}
if (tsconfig.options.experimentalDecorators !== true) {
tsconfigJson.compilerOptions.experimentalDecorators = true;
}
if (tsconfig.options.emitDecoratorMetadata !== true) {
tsconfigJson.compilerOptions.emitDecoratorMetadata = true;
}
if (context.options.output.dts) {
const dtsFilePath = context.options.output.dts ? context.options.output.dts.startsWith(context.options.workspaceRoot) ? context.options.output.dts : joinPaths(context.options.workspaceRoot, context.options.output.dts) : joinPaths(context.options.workspaceRoot, context.options.projectRoot, "storm.d.ts");
const dtsRelativePath = joinPaths(relativePath(joinPaths(context.options.workspaceRoot, context.options.projectRoot), findFilePath(dtsFilePath)), findFileName(dtsFilePath));
if (!tsconfigJson.include?.some((filePattern) => isIncludeMatchFound(filePattern, [
dtsFilePath,
dtsRelativePath,
"storm.d.ts"
]))) {
tsconfigJson.include ??= [];
tsconfigJson.include.push(dtsRelativePath.startsWith("./") ? dtsRelativePath.slice(2) : dtsRelativePath);
}
}
if (!tsconfig.options.lib?.some((lib) => [
"lib.esnext.d.ts",
"lib.es2021.d.ts",
"lib.es2022.d.ts",
"lib.es2023.d.ts"
].includes(lib.toLowerCase()))) {
tsconfigJson.compilerOptions.lib ??= [];
tsconfigJson.compilerOptions.lib.push("esnext");
}
if (tsconfig.options.module !== ts.ModuleKind.ESNext) {
tsconfigJson.compilerOptions.module = "ESNext";
}
if (!tsconfig.options.target || ![
ts.ScriptTarget.ESNext,
ts.ScriptTarget.ES2024,
ts.ScriptTarget.ES2023,
ts.ScriptTarget.ES2022,
ts.ScriptTarget.ES2021
].includes(tsconfig.options.target)) {
tsconfigJson.compilerOptions.target = "ESNext";
}
if (tsconfig.options.moduleResolution !== ts.ModuleResolutionKind.Bundler) {
tsconfigJson.compilerOptions.moduleResolution = "Bundler";
}
if (tsconfig.options.moduleDetection !== ts.ModuleDetectionKind.Force) {
tsconfigJson.compilerOptions.moduleDetection = "force";
}
if (tsconfig.options.allowSyntheticDefaultImports !== true) {
tsconfigJson.compilerOptions.allowSyntheticDefaultImports = true;
}
if (tsconfig.options.noImplicitOverride !== true) {
tsconfigJson.compilerOptions.noImplicitOverride = true;
}
if (tsconfig.options.noUncheckedIndexedAccess !== true) {
tsconfigJson.compilerOptions.noUncheckedIndexedAccess = true;
}
if (tsconfig.options.skipLibCheck !== true) {
tsconfigJson.compilerOptions.skipLibCheck = true;
}
if (tsconfig.options.resolveJsonModule !== true) {
tsconfigJson.compilerOptions.resolveJsonModule = true;
}
if (tsconfig.options.isolatedModules !== true) {
tsconfigJson.compilerOptions.isolatedModules = true;
}
if (tsconfig.options.verbatimModuleSyntax !== false) {
tsconfigJson.compilerOptions.verbatimModuleSyntax = false;
}
if (tsconfig.options.allowJs !== true) {
tsconfigJson.compilerOptions.allowJs = true;
}
if (tsconfig.options.esModuleInterop !== true) {
tsconfigJson.compilerOptions.esModuleInterop = true;
}
if (tsconfig.options.declaration !== true) {
tsconfigJson.compilerOptions.declaration = true;
}
if (context.options.platform === "browser") {
if (tsconfig.options.jsx !== ts.JsxEmit.ReactJSX) {
tsconfigJson.compilerOptions.jsx = "react-jsx";
}
if (!tsconfig.options.lib?.some((lib) => lib.toLowerCase() !== "dom")) {
tsconfigJson.compilerOptions.lib ??= [];
tsconfigJson.compilerOptions.lib.push("dom");
}
if (!tsconfig.options.lib?.some((lib) => lib.toLowerCase() !== "dom.iterable")) {
tsconfigJson.compilerOptions.lib ??= [];
tsconfigJson.compilerOptions.lib.push("dom.iterable");
}
} else if (context.options.platform === "node") {
if (!tsconfig.options.types?.some((type) => type.toLowerCase() === "node" || type.toLowerCase() === "@types/node")) {
tsconfigJson.compilerOptions.types ??= [];
tsconfigJson.compilerOptions.types.push("node");
}
}
return tsconfigJson;
}
__name(getTsconfigChanges, "getTsconfigChanges");
// src/commands/init/tsconfig/index.ts
async function initTsconfig(context, hooks) {
context.log(LogLevelLabel.TRACE, "Initializing TypeScript configuration for the Storm Stack project.");
if (!isPackageExists("typescript")) {
throw new Error('The TypeScript package is not installed. Please install the package using the command: "npm install typescript --save-dev"');
}
const originalTsconfigJson = await readJsonFile(context.options.tsconfig);
const json = await getTsconfigChanges(context);
await writeFile(context.log, context.options.tsconfig, StormJSON.stringify(json));
context.tsconfig = getParsedTypeScriptConfig(context.options.workspaceRoot, context.options.projectRoot, context.options.tsconfig, context.options.tsconfigRaw);
await hooks.callHook("init:tsconfig", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occured while resolving the TypeScript options: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occured while resolving the TypeScript options", {
cause: error
});
});
const tsconfigFilePath = getTsconfigFilePath(context.options.projectRoot, context.options.tsconfig);
const updateTsconfigJson = await readJsonFile(tsconfigFilePath);
if (updateTsconfigJson?.compilerOptions?.types && Array.isArray(updateTsconfigJson.compilerOptions.types) && !updateTsconfigJson.compilerOptions.types.length) {
delete updateTsconfigJson.compilerOptions.types;
}
const result = getObjectDiff(originalTsconfigJson, updateTsconfigJson, {
ignoreArrayOrder: true,
showOnly: {
statuses: [
"added",
"deleted",
"updated"
],
granularity: "deep"
}
});
const changes = [];
const getChanges = /* @__PURE__ */ __name((difference, property) => {
if (difference.status === "added" || difference.status === "deleted" || difference.status === "updated") {
if (difference.diff) {
for (const diff of difference.diff) {
getChanges(diff, property ? `${property}.${difference.property}` : difference.property);
}
} else {
changes.push({
field: property ? `${property}.${difference.property}` : difference.property,
status: difference.status,
previous: difference.status === "added" ? "---" : StormJSON.stringify(difference.previousValue),
current: difference.status === "deleted" ? "---" : StormJSON.stringify(difference.currentValue)
});
}
}
}, "getChanges");
for (const diff of result.diff) {
getChanges(diff);
}
if (changes.length > 0) {
context.log(LogLevelLabel.WARN, `Updating the following configuration values in "${tsconfigFilePath}" file:
${changes.map((change, i) => `${chalk.bold.whiteBright(`${i + 1}. ${titleCase(change.status)} the ${change.field} field: `)}
${chalk.red(` - Previous: ${change.previous} `)}
${chalk.green(` - Updated: ${change.current} `)}
`).join("\n")}
`);
}
await writeFile(context.log, tsconfigFilePath, StormJSON.stringify(updateTsconfigJson));
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.");
}
}
__name(initTsconfig, "initTsconfig");
// src/commands/init/index.ts
async function init(context, hooks) {
await hooks.callHook("init:begin", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occured while starting initialization for the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occured while starting initialization for the Storm Stack project", {
cause: error
});
});
context.unimport = createUnimport(context);
await context.unimport.init();
await initOptions(context, hooks);
await initInstall(context, hooks);
await initTsconfig(context, hooks);
const handlePreTransform = /* @__PURE__ */ __name(async (context2, sourceFile) => {
await hooks.callHook("build:pre-transform", context2, sourceFile).catch((error) => {
context2.log(LogLevelLabel.ERROR, `An error occured while pre-transforming the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occured while pre-transforming the Storm Stack project", {
cause: error
});
});
return sourceFile;
}, "handlePreTransform");
const handlePostTransform = /* @__PURE__ */ __name(async (context2, sourceFile) => {
await hooks.callHook("build:post-transform", context2, sourceFile).catch((error) => {
context2.log(LogLevelLabel.ERROR, `An error occured while post-transforming the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occured while post-transforming the Storm Stack project", {
cause: error
});
});
return sourceFile;
}, "handlePostTransform");
context.compiler = new Compiler(context, {
onPreTransform: handlePreTransform,
onPostTransform: handlePostTransform
});
await initEntry(context, hooks);
await initReflections(context, hooks);
await hooks.callHook("init:complete", context).catch((error) => {
context.log(LogLevelLabel.ERROR, `An error occured while finishing initialization for the Storm Stack project: ${error.message}
${error.stack ?? ""}`);
throw new Error("An error occured while finishing initialization for the Storm Stack project", {
cause: error
});
});
context.vfs[__VFS_INIT__]();
}
__name(init, "init");
export { init };
//# sourceMappingURL=chunk-JGDCG6II.js.map
//# sourceMappingURL=chunk-JGDCG6II.js.map