"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
function _path() {
  const data = _interopRequireDefault(require("path"));
  _path = function () {
    return data;
  };
  return data;
}
function _stream() {
  const data = require("stream");
  _stream = function () {
    return data;
  };
  return data;
}
function _nullthrows() {
  const data = _interopRequireDefault(require("nullthrows"));
  _nullthrows = function () {
    return data;
  };
  return data;
}
function _logger() {
  const data = _interopRequireWildcard(require("@parcel/logger"));
  _logger = function () {
    return data;
  };
  return data;
}
function _diagnostic() {
  const data = _interopRequireWildcard(require("@parcel/diagnostic"));
  _diagnostic = function () {
    return data;
  };
  return data;
}
function _utils() {
  const data = require("@parcel/utils");
  _utils = function () {
    return data;
  };
  return data;
}
function _rust() {
  const data = require("@parcel/rust");
  _rust = function () {
    return data;
  };
  return data;
}
var _Dependency = require("./Dependency");
var _ParcelConfig = _interopRequireDefault(require("./ParcelConfig"));
var _PathRequest = require("./requests/PathRequest");
var _Asset = require("./public/Asset");
var _UncommittedAsset = _interopRequireDefault(require("./UncommittedAsset"));
var _assetUtils = require("./assetUtils");
var _summarizeRequest = _interopRequireDefault(require("./summarizeRequest"));
var _PluginOptions = _interopRequireDefault(require("./public/PluginOptions"));
var _utils2 = require("./utils");
var _InternalConfig = require("./InternalConfig");
var _ConfigRequest = require("./requests/ConfigRequest");
var _DevDepRequest = require("./requests/DevDepRequest");
var _projectPath = require("./projectPath");
function _assert() {
  const data = _interopRequireDefault(require("assert"));
  _assert = function () {
    return data;
  };
  return data;
}
function _profiler() {
  const data = require("@parcel/profiler");
  _profiler = function () {
    return data;
  };
  return data;
}
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// TODO: eventually call path request as sub requests
class Transformation {
  constructor({
    request,
    options,
    config,
    workerApi
  }) {
    this.configs = new Map();
    this.parcelConfig = config;
    this.options = options;
    this.request = request;
    this.workerApi = workerApi;
    this.invalidations = (0, _utils2.createInvalidations)();
    this.devDepRequests = new Map();
    this.pluginDevDeps = [];
    this.resolverRunner = new _PathRequest.ResolverRunner({
      config,
      options,
      previousDevDeps: request.devDeps
    });
    this.pluginOptions = new _PluginOptions.default((0, _utils2.optionsProxy)(this.options, option => {
      this.invalidations.invalidateOnOptionChange.add(option);
    }, devDep => {
      this.pluginDevDeps.push(devDep);
    }));
  }
  async run() {
    let asset = await this.loadAsset();
    let existing;
    if (!asset.mapBuffer && _utils().SOURCEMAP_EXTENSIONS.has(asset.value.type)) {
      // Load existing sourcemaps, this automatically runs the source contents extraction
      try {
        existing = await asset.loadExistingSourcemap();
      } catch (err) {
        _logger().default.verbose([{
          origin: '@parcel/core',
          message: (0, _diagnostic().md)`Could not load existing source map for ${(0, _projectPath.fromProjectPathRelative)(asset.value.filePath)}`
        }, {
          origin: '@parcel/core',
          message: (0, _diagnostic().escapeMarkdown)(err.message)
        }]);
      }
    }
    if (existing == null &&
    // Don't buffer an entire stream into memory since it may not need sourceContent,
    // e.g. large binary files
    !(asset.content instanceof _stream().Readable)) {
      // If no existing sourcemap was found, initialize asset.sourceContent
      // with the original contents. This will be used when the transformer
      // calls setMap to ensure the source content is in the sourcemap.
      asset.sourceContent = await asset.getCode();
    }
    (0, _DevDepRequest.invalidateDevDeps)(this.request.invalidDevDeps, this.options, this.parcelConfig);
    let pipeline = await this.loadPipeline(this.request.filePath, asset.value.isSource, asset.value.pipeline);
    let assets, error;
    try {
      let results = await this.runPipelines(pipeline, asset);
      await Promise.all(results.map(asset => asset.commit()));
      assets = results.map(a => a.value);
    } catch (e) {
      error = e;
    }
    let configRequests = (0, _ConfigRequest.getConfigRequests)([...this.configs.values(), ...this.resolverRunner.configs.values()]);
    let devDepRequests = (0, _DevDepRequest.getWorkerDevDepRequests)([...this.devDepRequests.values(), ...this.resolverRunner.devDepRequests.values()]);

    // $FlowFixMe because of $$raw
    return {
      $$raw: true,
      assets,
      configRequests,
      // When throwing an error, this (de)serialization is done automatically by the WorkerFarm
      error: error ? (0, _diagnostic().anyToDiagnostic)(error) : undefined,
      invalidations: this.invalidations,
      devDepRequests
    };
  }
  async loadAsset() {
    let {
      filePath,
      env,
      code,
      pipeline,
      isSource: isSourceOverride,
      sideEffects,
      query
    } = this.request;
    let {
      content,
      size,
      isSource: summarizedIsSource
    } = await (0, _summarizeRequest.default)(this.options.inputFS, {
      filePath: (0, _projectPath.fromProjectPath)(this.options.projectRoot, filePath),
      code
    });

    // Prefer `isSource` originating from the AssetRequest.

    // If the transformer request passed code, use a hash in addition
    // to the filename as the base for the id to ensure it is unique.
    let idBase = (0, _projectPath.fromProjectPathRelative)(filePath);
    if (code != null) {
      idBase += (0, _rust().hashString)(code);
    }
    return new _UncommittedAsset.default({
      idBase,
      value: (0, _assetUtils.createAsset)(this.options.projectRoot, {
        idBase,
        filePath,
        isSource: isSourceOverride ?? summarizedIsSource,
        type: _path().default.extname((0, _projectPath.fromProjectPathRelative)(filePath)).slice(1),
        pipeline,
        env,
        query,
        stats: {
          time: 0,
          size
        },
        sideEffects
      }),
      options: this.options,
      content,
      invalidations: this.invalidations
    });
  }
  async runPipelines(pipeline, initialAsset) {
    let initialType = initialAsset.value.type;
    let assets;
    try {
      assets = await this.runPipeline(pipeline, initialAsset);
    } finally {
      // Add dev dep requests for each transformer
      for (let transformer of pipeline.transformers) {
        await this.addDevDependency({
          specifier: transformer.name,
          resolveFrom: transformer.resolveFrom,
          range: transformer.range
        });
      }

      // Add dev dep requests for dependencies of transformer plugins
      // (via proxied packageManager.require calls).
      for (let devDep of this.pluginDevDeps) {
        await this.addDevDependency(devDep);
      }
    }
    let finalAssets = [];
    for (let asset of assets) {
      let nextPipeline;
      if (asset.value.type !== initialType) {
        nextPipeline = await this.loadNextPipeline({
          filePath: initialAsset.value.filePath,
          isSource: asset.value.isSource,
          newType: asset.value.type,
          newPipeline: asset.value.pipeline,
          currentPipeline: pipeline
        });
      }
      if (nextPipeline) {
        let nextPipelineAssets = await this.runPipelines(nextPipeline, asset);
        finalAssets = finalAssets.concat(nextPipelineAssets);
      } else {
        finalAssets.push(asset);
      }
    }
    if (!pipeline.postProcess) {
      return finalAssets;
    }
    (0, _assert().default)(pipeline.postProcess != null);
    let processedFinalAssets = (await pipeline.postProcess(finalAssets)) ?? [];
    return processedFinalAssets;
  }
  async addDevDependency(opts) {
    let {
      specifier,
      resolveFrom,
      range
    } = opts;
    let key = `${specifier}:${(0, _projectPath.fromProjectPathRelative)(resolveFrom)}`;
    if (this.devDepRequests.has(key)) {
      return;
    }

    // Ensure that the package manager has an entry for this resolution.
    try {
      await this.options.packageManager.resolve(specifier, (0, _projectPath.fromProjectPath)(this.options.projectRoot, opts.resolveFrom), {
        range
      });
    } catch (err) {
      // ignore
    }
    let devDepRequest = await (0, _DevDepRequest.createDevDependency)(opts, this.request.devDeps, this.options);
    this.devDepRequests.set(key, devDepRequest);
  }
  async runPipeline(pipeline, initialAsset) {
    if (pipeline.transformers.length === 0) {
      return [initialAsset];
    }
    let initialType = initialAsset.value.type;
    let inputAssets = [initialAsset];
    let resultingAssets = [];
    let finalAssets = [];
    for (let transformer of pipeline.transformers) {
      resultingAssets = [];
      for (let asset of inputAssets) {
        if (asset.value.type !== initialType && (await this.loadNextPipeline({
          filePath: initialAsset.value.filePath,
          isSource: asset.value.isSource,
          newType: asset.value.type,
          newPipeline: asset.value.pipeline,
          currentPipeline: pipeline
        }))) {
          finalAssets.push(asset);
          continue;
        }
        try {
          const measurement = _profiler().tracer.createMeasurement(transformer.name, 'transform', (0, _projectPath.fromProjectPathRelative)(initialAsset.value.filePath));
          let transformerResults = await this.runTransformer(pipeline, asset, transformer.plugin, transformer.name, transformer.config, transformer.configKeyPath, this.parcelConfig);
          measurement && measurement.end();
          for (let result of transformerResults) {
            if (result instanceof _UncommittedAsset.default) {
              resultingAssets.push(result);
              continue;
            }
            resultingAssets.push(asset.createChildAsset(result, transformer.name, this.parcelConfig.filePath, transformer.configKeyPath));
          }
        } catch (e) {
          let diagnostic = (0, _diagnostic().errorToDiagnostic)(e, {
            origin: transformer.name,
            filePath: (0, _projectPath.fromProjectPath)(this.options.projectRoot, asset.value.filePath)
          });

          // If this request is a virtual asset that might not exist on the filesystem,
          // add the `code` property to each code frame in the diagnostics that match the
          // request's filepath. This can't be done by the transformer because it might not
          // have access to the original code (e.g. an inline script tag in HTML).
          if (this.request.code != null) {
            for (let d of diagnostic) {
              if (d.codeFrames) {
                for (let codeFrame of d.codeFrames) {
                  if (codeFrame.code == null && codeFrame.filePath === this.request.filePath) {
                    codeFrame.code = this.request.code;
                  }
                }
              }
            }
          }
          throw new (_diagnostic().default)({
            diagnostic
          });
        }
      }
      inputAssets = resultingAssets;
    }

    // Make assets with ASTs generate unless they are CSS modules. This parallelizes generation
    // and distributes work more evenly across workers than if one worker needed to
    // generate all assets in a large bundle during packaging.
    await Promise.all(resultingAssets.filter(asset => asset.ast != null && !(this.options.mode === 'production' && asset.value.type === 'css' && asset.value.symbols)).map(async asset => {
      if (asset.isASTDirty && asset.generate) {
        var _output$map;
        let output = await asset.generate();
        asset.content = output.content;
        asset.mapBuffer = (_output$map = output.map) === null || _output$map === void 0 ? void 0 : _output$map.toBuffer();
      }
      asset.clearAST();
    }));
    return finalAssets.concat(resultingAssets);
  }
  async loadPipeline(filePath, isSource, pipeline) {
    let transformers = await this.parcelConfig.getTransformers(filePath, pipeline, this.request.isURL);
    for (let transformer of transformers) {
      let config = await this.loadTransformerConfig(transformer, isSource);
      if (config) {
        this.configs.set(transformer.name, config);
      }
    }
    return {
      id: transformers.map(t => t.name).join(':'),
      transformers: transformers.map(transformer => {
        var _this$configs$get;
        return {
          name: transformer.name,
          resolveFrom: transformer.resolveFrom,
          config: (_this$configs$get = this.configs.get(transformer.name)) === null || _this$configs$get === void 0 ? void 0 : _this$configs$get.result,
          configKeyPath: transformer.keyPath,
          plugin: transformer.plugin
        };
      }),
      options: this.options,
      pluginOptions: this.pluginOptions,
      workerApi: this.workerApi
    };
  }
  async loadNextPipeline({
    filePath,
    isSource,
    newType,
    newPipeline,
    currentPipeline
  }) {
    let filePathRelative = (0, _projectPath.fromProjectPathRelative)(filePath);
    let nextFilePath = (0, _projectPath.toProjectPathUnsafe)(filePathRelative.slice(0, -_path().default.extname(filePathRelative).length) + '.' + newType);
    let nextPipeline = await this.loadPipeline(nextFilePath, isSource, newPipeline);
    if (nextPipeline.id === currentPipeline.id) {
      return null;
    }
    return nextPipeline;
  }
  async loadTransformerConfig(transformer, isSource) {
    let loadConfig = transformer.plugin.loadConfig;
    if (!loadConfig) {
      return;
    }
    let config = (0, _InternalConfig.createConfig)({
      plugin: transformer.name,
      isSource,
      searchPath: this.request.filePath,
      env: this.request.env
    });
    await (0, _ConfigRequest.loadPluginConfig)(transformer, config, this.options);
    for (let devDep of config.devDeps) {
      await this.addDevDependency(devDep);
    }
    return config;
  }
  async runTransformer(pipeline, asset, transformer, transformerName, preloadedConfig, configKeyPath, parcelConfig) {
    var _transformer$parse;
    if (asset.transformers.has(transformerName)) {
      return [asset];
    }
    asset.transformers.add(transformerName);
    const logger = new (_logger().PluginLogger)({
      origin: transformerName
    });
    const tracer = new (_profiler().PluginTracer)({
      origin: transformerName,
      category: 'transform'
    });
    const resolve = async (from, to, options) => {
      let result = await this.resolverRunner.resolve((0, _Dependency.createDependency)(this.options.projectRoot, {
        env: asset.value.env,
        specifier: to,
        specifierType: (options === null || options === void 0 ? void 0 : options.specifierType) || 'esm',
        packageConditions: options === null || options === void 0 ? void 0 : options.packageConditions,
        sourcePath: from
      }));
      if (result.invalidateOnFileCreate) {
        this.invalidations.invalidateOnFileCreate.push(...result.invalidateOnFileCreate.map(i => (0, _utils2.invalidateOnFileCreateToInternal)(this.options.projectRoot, i)));
      }
      if (result.invalidateOnFileChange) {
        for (let filePath of result.invalidateOnFileChange) {
          this.invalidations.invalidateOnFileChange.add((0, _projectPath.toProjectPath)(this.options.projectRoot, filePath));
        }
      }
      if (result.diagnostics && result.diagnostics.length > 0) {
        throw new (_diagnostic().default)({
          diagnostic: result.diagnostics
        });
      }
      return (0, _projectPath.fromProjectPath)(this.options.projectRoot, (0, _nullthrows().default)(result.assetGroup).filePath);
    };

    // If an ast exists on the asset, but we cannot reuse it,
    // use the previous transform to generate code that we can re-parse.
    if (asset.ast && asset.isASTDirty && (!transformer.canReuseAST || !transformer.canReuseAST({
      ast: asset.ast,
      options: pipeline.pluginOptions,
      logger,
      tracer
    })) && asset.generate) {
      var _output$map2;
      let output = await asset.generate();
      asset.content = output.content;
      asset.mapBuffer = (_output$map2 = output.map) === null || _output$map2 === void 0 ? void 0 : _output$map2.toBuffer();
    }

    // Load config for the transformer.
    let config = preloadedConfig;

    // Parse if there is no AST available from a previous transform.
    let parse = (_transformer$parse = transformer.parse) === null || _transformer$parse === void 0 ? void 0 : _transformer$parse.bind(transformer);
    if (!asset.ast && parse) {
      let ast = await parse({
        asset: new _Asset.Asset(asset),
        config,
        options: pipeline.pluginOptions,
        resolve,
        logger,
        tracer
      });
      if (ast) {
        asset.setAST(ast);
        asset.isASTDirty = false;
      }
    }

    // Transform.
    let transfomerResult =
    // $FlowFixMe the returned IMutableAsset really is a MutableAsset
    await transformer.transform({
      asset: new _Asset.MutableAsset(asset),
      config,
      options: pipeline.pluginOptions,
      resolve,
      logger,
      tracer
    });
    let results = await normalizeAssets(this.options, transfomerResult);

    // Create generate and postProcess function that can be called later
    asset.generate = () => {
      let publicAsset = new _Asset.Asset(asset);
      if (transformer.generate && asset.ast) {
        let generated = transformer.generate({
          asset: publicAsset,
          ast: asset.ast,
          options: pipeline.pluginOptions,
          logger,
          tracer
        });
        asset.clearAST();
        return Promise.resolve(generated);
      }
      throw new Error('Asset has an AST but no generate method is available on the transform');
    };
    let postProcess = transformer.postProcess;
    if (postProcess) {
      pipeline.postProcess = async assets => {
        let results = await postProcess.call(transformer, {
          assets: assets.map(asset => new _Asset.MutableAsset(asset)),
          config,
          options: pipeline.pluginOptions,
          resolve,
          logger,
          tracer
        });
        return Promise.all(results.map(result => asset.createChildAsset(result, transformerName, parcelConfig.filePath
        // configKeyPath,
        )));
      };
    }
    return results;
  }
}
exports.default = Transformation;
function normalizeAssets(options, results) {
  return results.map(result => {
    if (result instanceof _Asset.MutableAsset) {
      return (0, _Asset.mutableAssetToUncommittedAsset)(result);
    }
    return result;
  });
}