'use strict';

var chunkOTH2GDR2_cjs = require('./chunk-OTH2GDR2.cjs');
var chunk3MH6X7IX_cjs = require('./chunk-3MH6X7IX.cjs');
var chunkVPBRJQ7F_cjs = require('./chunk-VPBRJQ7F.cjs');
var chunkUJJENY47_cjs = require('./chunk-UJJENY47.cjs');
var chunkY42FCFTW_cjs = require('./chunk-Y42FCFTW.cjs');
var chunkSQMHEI3Z_cjs = require('./chunk-SQMHEI3Z.cjs');
var chunkAF3E2D32_cjs = require('./chunk-AF3E2D32.cjs');
var typeCompiler = require('@deepkit/type-compiler');
var types = require('@storm-software/config-tools/types');
var hash = require('@stryke/hash/hash');
var joinPaths = require('@stryke/path/join-paths');
var ts = require('typescript');
var napi = require('@ast-grep/napi');
var mutex$1 = require('@stryke/helpers/mutex');
var readFile = require('@stryke/fs/read-file');
var stormJson = require('@stryke/json/storm-json');
var exists = require('@stryke/path/exists');
var deepClone = require('@stryke/helpers/deep-clone');
var isEqual = require('@stryke/helpers/is-equal');
var defu = require('defu');
var fs = require('fs');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var ts__default = /*#__PURE__*/_interopDefault(ts);
var defu__default = /*#__PURE__*/_interopDefault(defu);

async function writeDotenvReflection(log, context, reflection, name) {
  let reflectionObjectLiteral = (void 0).from({
    kind: (void 0).objectLiteral,
    description: `An object containing the dotenv variables used by the ${context.name ? `${context.name} application` : "application"}.`,
    types: []
  });
  const varsFilePath = chunkOTH2GDR2_cjs.getDotenvReflectionsPath(context, name);
  if (exists.existsSync(varsFilePath)) {
    reflectionObjectLiteral = (void 0)((void 0)(await readFile.readJsonFile(varsFilePath)));
  }
  reflection.getProperties().map((prop) => {
    if (!reflectionObjectLiteral.hasProperty(prop.getName())) {
      reflectionObjectLiteral.addProperty(prop.property);
    }
  });
  const serialized = reflectionObjectLiteral.serializeType();
  return chunkUJJENY47_cjs.writeFile(log, chunkOTH2GDR2_cjs.getDotenvReflectionsPath(context, name), stormJson.StormJSON.stringify(serialized));
}
chunkAF3E2D32_cjs.__name(writeDotenvReflection, "writeDotenvReflection");
async function writeDotenvProperties(log, context, name, properties) {
  const varsFilePath = chunkOTH2GDR2_cjs.getDotenvPath(context, name);
  let reflectionObjectLiteral = (void 0).from({
    kind: (void 0).objectLiteral,
    description: `An object containing the dotenv variables used by the ${context.name ? `${context.name} application` : "application"}.`,
    types: []
  });
  if (exists.existsSync(varsFilePath)) {
    reflectionObjectLiteral = (void 0)((void 0)(await readFile.readJsonFile(varsFilePath)));
  }
  properties.map((prop) => {
    if (!reflectionObjectLiteral.hasProperty(prop.getName())) {
      reflectionObjectLiteral.addProperty(prop.property);
    }
  });
  await chunkUJJENY47_cjs.writeFile(log, varsFilePath, stormJson.StormJSON.stringify(reflectionObjectLiteral.serializeType()));
}
chunkAF3E2D32_cjs.__name(writeDotenvProperties, "writeDotenvProperties");

// src/helpers/transform/transform-env.ts
var mutex = new mutex$1.Mutex();
async function transformEnv(log, source, context) {
  const varsReflection = await chunkOTH2GDR2_cjs.resolveDotenvReflection(context, "variables");
  const dotenvProperties = await chunkOTH2GDR2_cjs.resolveDotenvProperties(log, context, "variables");
  const originalLength = dotenvProperties.length;
  const ast = await napi.parseAsync(napi.Lang.TypeScript, source.code.toString());
  const root = ast.root();
  const nodes = root.findAll({
    rule: {
      kind: "member_expression",
      any: [
        {
          pattern: "$storm.env.$ENV_VALUE"
        },
        {
          pattern: "process.env.$ENV_VALUE"
        },
        {
          pattern: "input.meta.$ENV_VALUE"
        }
      ]
    }
  });
  if (!nodes) {
    return source;
  }
  for (const node of nodes) {
    const name = node.getMatch("ENV_VALUE")?.text()?.replace("STORM_", "");
    if (name && varsReflection.hasProperty(name)) {
      const reflectionProperty = varsReflection.getProperty(name);
      if (context.resolvedDotenv.replace) {
        const value = context.resolvedDotenv.values?.[name] ?? context.resolvedDotenv.values?.[`STORM_${name}`] ?? reflectionProperty.getDefaultValue();
        if (reflectionProperty.isValueRequired() && value === void 0) {
          throw new Error(`Environment variable \`${name}\` is not defined in the .env configuration files`);
        }
        source.code = source.code.replaceAll(node.text(), String(value));
      }
      dotenvProperties.push(reflectionProperty);
    } else {
      throw new Error(`Environment variable \`${name}\` is not defined in the dotenv type definition but is used in the code. 

The following variable names are defined in the dotenv type definition: 
${Object.keys(varsReflection).map((typeDef) => ` - ${typeDef} `).join("\n")} 

Please check your \`dotenv\` configuration option. If you are using a custom dotenv type definition, please make sure that the variable names match the ones in the code. 

`);
    }
  }
  if (dotenvProperties.length !== originalLength) {
    log(types.LogLevelLabel.INFO, `Adding environment variables from ${source.id} to variables.json.`);
    await mutex.acquire();
    try {
      await writeDotenvProperties(log, context, "variables", dotenvProperties);
    } catch (e) {
      console.error(e);
    } finally {
      mutex.release();
    }
  }
  return source;
}
chunkAF3E2D32_cjs.__name(transformEnv, "transformEnv");
var mutex2 = new mutex$1.Mutex();
function getLatestErrorCode(type = "general", errorCodes = {}) {
  if (!errorCodes[type]) {
    return "1";
  }
  const keys = Object.keys(errorCodes[type]);
  if (keys.length === 0) {
    return "1";
  }
  const errorCode = Number.parseInt(keys.sort((a, b) => b.localeCompare(a))[0]);
  if (Number.isNaN(errorCode)) {
    return "1";
  }
  return String(errorCode + 1);
}
chunkAF3E2D32_cjs.__name(getLatestErrorCode, "getLatestErrorCode");
async function transformErrors(log, source, context) {
  if (!context.workspaceConfig.error.codesFile) {
    log(types.LogLevelLabel.WARN, `No error codes file path configured in \`storm-workspace.json\`. Skipping error transformation processing.`);
    return source;
  }
  let errorCodes = {};
  if (fs.existsSync(joinPaths.joinPaths(context.workspaceConfig.workspaceRoot, context.workspaceConfig.error.codesFile))) {
    errorCodes = await readFile.readJsonFile(joinPaths.joinPaths(context.workspaceConfig.workspaceRoot, context.workspaceConfig.error.codesFile));
  }
  const originalErrorCodes = deepClone.deepClone(errorCodes);
  const ast = await napi.parseAsync(napi.Lang.TypeScript, source.code.toString());
  const root = ast.root();
  const nodes = root.findAll({
    rule: {
      kind: "new_expression",
      any: [
        {
          pattern: "new Error($$$ARGS)"
        },
        {
          pattern: "new TypeError($$$ARGS)"
        },
        {
          pattern: "new ReferenceError($$$ARGS)"
        },
        {
          pattern: "new SyntaxError($$$ARGS)"
        },
        {
          pattern: "new RangeError($$$ARGS)"
        },
        {
          pattern: "new EvalError($$$ARGS)"
        },
        {
          pattern: "new URIError($$$ARGS)"
        },
        {
          pattern: "new AggregateError($$$ARGS)"
        },
        {
          pattern: "new InternalError($$$ARGS)"
        },
        {
          pattern: "new SystemError($$$ARGS)"
        },
        {
          pattern: "new NotImplementedError($$$ARGS)"
        },
        {
          pattern: "new StormError($$$ARGS)"
        }
      ]
    }
  });
  if (!nodes || nodes.length === 0) {
    log(types.LogLevelLabel.TRACE, `No error messages require transformation in ${source.id}.`);
    return source;
  }
  for (const node of nodes) {
    const args = node.getMultipleMatches("ARGS").filter((arg) => arg.kind() === "string" || arg.kind() === "template_string");
    if (args.length >= 1 && args[0]) {
      let message = "";
      const params = [];
      if (args[0].kind() === "string") {
        message = args[0].text().slice(1, -1);
      } else if (args[0].kind() === "template_string") {
        for (const node2 of args[0].children()) {
          if (node2.kind() === "string_fragment") {
            message += node2.text();
          } else if (node2.kind() === "template_substitution") {
            const param = node2.text().slice(2, -1);
            if (param) {
              params.push(param);
              message += "%s";
            }
          }
        }
      }
      if (message) {
        let type = "general";
        if (args.length > 1 && args[1]) {
          const text = args[1].text();
          if (text) {
            type = text;
          }
        }
        errorCodes[type] ??= {};
        let code = Object.keys(errorCodes[type]).find((key) => errorCodes[type][key] === message);
        if (!code) {
          code = getLatestErrorCode(type, errorCodes);
          log(types.LogLevelLabel.INFO, `Found unlisted error message "${message}" in ${source.id}. Assigning it to "${type}" code #${code}.`);
          errorCodes[type][code] = message;
        }
        source.code = source.code.replaceAll(node.text(), `new StormError({ type: "${type}", code: ${code}${params.length > 0 ? ` params: [${params.join(", ")}] ` : ""} })`);
      }
    }
  }
  if (!isEqual.isEqual(originalErrorCodes, errorCodes)) {
    log(types.LogLevelLabel.INFO, `Adding error messages from ${source.id} to ${context.workspaceConfig.error.codesFile}.`);
    await mutex2.acquire();
    try {
      await chunkUJJENY47_cjs.writeFile(log, joinPaths.joinPaths(context.workspaceConfig.workspaceRoot, context.workspaceConfig.error.codesFile), stormJson.StormJSON.stringify(defu__default.default(errorCodes, await readFile.readJsonFile(joinPaths.joinPaths(context.workspaceConfig.workspaceRoot, context.workspaceConfig.error.codesFile)))));
    } finally {
      mutex2.release();
    }
  }
  return source;
}
chunkAF3E2D32_cjs.__name(transformErrors, "transformErrors");

// src/compiler.ts
var Compiler = class {
  static {
    chunkAF3E2D32_cjs.__name(this, "Compiler");
  }
  #cache = /* @__PURE__ */ new WeakMap();
  /**
  * The logger function to use
  */
  log;
  /**
  * The cache directory
  */
  cacheDir;
  /**
  * A callback function to be called before the source file is compiled
  */
  onTransformCallback;
  constructor(context, onTransformCallback = async () => {
  }) {
    this.log = chunk3MH6X7IX_cjs.createLog("compiler", context);
    this.onTransformCallback = onTransformCallback;
    this.cacheDir = joinPaths.joinPaths(context.envPaths.cache, hash.hash(context.resolvedTsconfig.options));
  }
  /**
  * Transpile the module.
  *
  * @param context - The compiler context.
  * @param id - The name of the file to compile.
  * @param code - The source code to compile.
  * @returns The transpiled module.
  */
  async transpile(context, id, code, options = {}) {
    const source = this.getSourceFile(id, code);
    return this.transpileModule(context, source, options);
  }
  /**
  * Compile the source code.
  *
  * @param context - The compiler context.
  * @param id - The name of the file to compile.
  * @param code - The source code to compile.
  * @returns The compiled source code and source map.
  */
  async compile(context, id, code, options = {}) {
    this.log(types.LogLevelLabel.TRACE, `Compiling ${id}`);
    const source = this.getSourceFile(id, code);
    let transpiled;
    if (!options.skipCache) {
      transpiled = await this.getCache(context, source);
      if (transpiled) {
        this.log(types.LogLevelLabel.TRACE, `Cache hit: ${source.id}`);
      } else {
        this.log(types.LogLevelLabel.TRACE, `Cache miss: ${source.id}`);
      }
    }
    if (!transpiled) {
      transpiled = await this.transpileModule(context, source, options);
      await this.setCache(context, source, transpiled);
    }
    return transpiled;
  }
  /**
  * Get the source file.
  *
  * @param id - The name of the file.
  * @param code - The source code.
  * @returns The source file.
  */
  getSourceFile(id, code) {
    return {
      id,
      code: chunkSQMHEI3Z_cjs.getMagicString(code),
      env: []
    };
  }
  /**
  * Get the result of the compiler.
  *
  * @param sourceFile - The source file.
  * @param transpiled - The transpiled source code.
  * @returns The result of the compiler.
  */
  getResult(sourceFile, transpiled) {
    return chunkVPBRJQ7F_cjs.generateSourceMap(sourceFile.id, sourceFile.code, transpiled);
  }
  async getCache(context, sourceFile) {
    let cache = this.#cache.get(sourceFile);
    if (cache) {
      return cache;
    }
    if (context.skipCache) {
      return;
    }
    cache = await chunkY42FCFTW_cjs.getCache(sourceFile, this.cacheDir);
    if (cache) {
      this.#cache.set(sourceFile, cache);
    }
    return cache;
  }
  async setCache(context, sourceFile, transpiled) {
    if (transpiled) {
      this.#cache.set(sourceFile, transpiled);
    } else {
      this.#cache.delete(sourceFile);
    }
    if (context.skipCache) {
      return;
    }
    return chunkY42FCFTW_cjs.setCache(sourceFile, this.cacheDir, transpiled);
  }
  /**
  * Transpile the module.
  *
  * @param context - The compiler context.
  * @param source - The compiler source file.
  * @returns The transpiled module.
  */
  async transpileModule(context, source, options = {}) {
    this.log(types.LogLevelLabel.TRACE, `Transpiling ${source.id} module with TypeScript compiler`);
    let transformed = source;
    if (!options.skipTransform) {
      if (!options.skipEnvTransform) {
        transformed = await transformEnv(this.log, source, context);
      }
      if (!options.skipErrorsTransform) {
        transformed = await transformErrors(this.log, transformed, context);
      }
      await this.onTransformCallback(context, transformed);
    }
    if (context.unimport && !transformed.id.replaceAll("\\", "/").includes(context.runtimeDir)) {
      transformed = await context.unimport.injectImports(transformed);
    }
    const transpiled = ts__default.default.transpileModule(transformed.code.toString(), {
      compilerOptions: {
        ...context.resolvedTsconfig.options,
        configFilePath: context.tsconfig
      },
      fileName: transformed.id,
      transformers: {
        before: [
          typeCompiler.transformer
        ],
        after: [
          typeCompiler.declarationTransformer
        ]
      }
    });
    if (transpiled === null) {
      this.log(types.LogLevelLabel.ERROR, `Transform is null: ${transformed.id}`);
      throw new Error(`Transform is null: ${transformed.id}`);
    } else {
      this.log(types.LogLevelLabel.TRACE, `Transformed: ${transformed.id}`);
    }
    return transpiled.outputText;
  }
};

exports.Compiler = Compiler;
exports.writeDotenvReflection = writeDotenvReflection;
//# sourceMappingURL=chunk-TKKDUSJ2.cjs.map
//# sourceMappingURL=chunk-TKKDUSJ2.cjs.map