text: const Parser = require('./Parser');
  const path = require('path');
  const {errorUtils} = require('@parcel/utils');
  
  /**
   * A Pipeline composes multiple Asset types together.
   */
  class Pipeline {
    constructor(options) {
      this.options = options;
      this.parser = new Parser(options);
    }
  
    async process(path, custom, isWarmUp) {
      let options = this.options;
      if (isWarmUp) {
        options = Object.assign({isWarmUp}, options);
      }
  
      let asset = this.parser.getAsset(path, options);
      asset.contents = custom.contents;
      asset.ast = custom.ast;
      for (let prop in custom.props) {
        asset.options.vs[prop] = custom.props[prop];
      }
  
      let error = null;
      let generatedMap = {};
      let ast, generated;
      try {
        ({generated, ast} = await this.processAsset(asset));
        for (let rendition of generated) {
          generatedMap[rendition.type] = rendition.value;
        }
      } catch (err) {
        error = errorUtils.errorToJson(err);
        error.fileName = path;
      }
  
      return {
        id: asset.id,
        dependencies: Array.from(asset.dependencies.values()),
        generated: generatedMap,
        sourceMaps: asset.sourceMaps,
        error: error,
        hash: asset.hash,
        cacheData: asset.cacheData,
        ast
      };
    }
  
    async processAsset(asset) {
      let valGenerated;
      try {
        valGenerated = await asset.process();
      } catch (err) {
        throw asset.generateErrorMessage(err);
      }
  
      // early return during evaluation
      if (asset.options.vs.toVal) {
        asset.generated = valGenerated;
        asset.hash = await asset.generateHash();
        return {generated: valGenerated, ast: asset.ast};
      }
  
      let inputType = path.extname(asset.name).slice(1);
      let generated = [];
      let astJS = null;
      for (let rendition of this.iterateRenditions(asset)) {
        let {type, value} = rendition;
        if (typeof value !== 'string' || rendition.final) {
          generated.push(rendition);
          continue;
        }
  
        // Find an asset type for the rendition type.
        // If the asset is not already an instance of this asset type, process it.
        let AssetType = this.parser.findParser(
          asset.name.slice(0, -inputType.length) + type,
          true
        );
        if (!(asset instanceof AssetType)) {
          let opts = Object.assign({}, asset.options, {rendition});
          let subAsset = new AssetType(asset.name, opts);
          subAsset.id = asset.id;
          subAsset.contents = value;
          subAsset.dependencies = asset.dependencies;
          subAsset.cacheData = Object.assign(asset.cacheData, subAsset.cacheData);
  
          let {generated: processed, ast} = await this.processAsset(subAsset);
  
          if (
            (asset.constructor.name === 'TypeScriptAsset' &&
              subAsset.constructor.name === 'JSAsset') ||
            (asset.constructor.name === 'CoffeeScriptAsset' &&
              subAsset.constructor.name === 'JSAsset') ||
            (asset.constructor.name === 'VueAsset' &&
              subAsset.constructor.name === 'JSAsset')
          ) {
            astJS = ast;
          }
  
          if (rendition.meta) {
            for (let res of processed) {
              res.meta = rendition.meta;
              res.isMain = res.type === subAsset.type;
            }
          }
  
          generated = generated.concat(processed);
        } else {
          generated.push(rendition);
        }
      }
  
      // Post process. This allows assets a chance to modify the output produced by sub-asset types.
      try {
        if (!asset.options.vs.toVal) {
          // TODO: remove this check, early return now
          generated = await asset.postProcess(generated);
        }
      } catch (err) {
        throw asset.generateErrorMessage(err);
      }
  
      let hasMap = false;
      let sourceMaps = {};
      for (let rendition of generated) {
        if (rendition.map && rendition.type == asset.type) {
          sourceMaps[rendition.type] = rendition.map;
          hasMap = true;
        }
      }
  
      if (hasMap) {
        asset.sourceMaps = sourceMaps;
      }
  
      asset.generated = generated;
      asset.hash = await asset.generateHash();
  
      return {generated, ast: astJS || asset.ast};
    }
  
    *iterateRenditions(asset) {
      if (Array.isArray(asset.generated)) {
        return yield* asset.generated;
      }
  
      if (typeof asset.generated === 'string') {
        return yield {
          type: asset.type,
          value: asset.generated
        };
      }
  
      // Backward compatibility support for the old API.
      // Assume all renditions are final - don't compose asset types together.
      for (let type in asset.generated) {
        yield {
          type,
          value: asset.generated[type],
          // for scope hoisting, we need to post process all JS
          final: !(type === 'js' && this.options.scopeHoist)
        };
      }
    }
  }
  
  module.exports = Pipeline;
  