Repository: parcel-bundler/parcel
File: packages/transformers/elm/src/ElmTransformer.js (JavaScript)

Code before:
    if (config.optimize) code = minifyElmOutput(code);

    asset.type = '.js';
    asset.setCode(code);
    return [asset];
  },
});

async function compileToString(asset, options, config) {
  const installPackage = name => {
    if (!options.autoinstall) {
      throw new ThrowableDiagnostic({
        diagnostic: {
          message: `Denpendency '${name}' is not installed and autoinstall is turned off. Either install dependency manually or enable autoinstall`,
          origin: '@parcel/package-manager',
        },
      });
    }

    return options.packageManager.resolve(name, asset.filePath);
  };
  const pathToElmBin = await pathToElm(options.projectRoot, installPackage);
  await ensureElmJson(asset, pathToElmBin);

  const compileOptions = {
    cwd: config.cwd,
    debug: config.debug,
    optimize: config.optimize,
    pathToElm: pathToElmBin,
  };
  return elm.compileToString(asset.filePath, compileOptions);
}

async function pathToElm(root, installPackage) {
  if (!commandExists.sync('elm')) {
    const elmBin = path.resolve(root, 'node_modules/elm/bin/elm');

    if (!fs.existsSync(elmBin)) await installPackage('elm');
    return elmBin;
  }

  return undefined; // use globally installed elm
}

async function ensureElmJson(asset, pathToElmBin) {
  const elmJson = await asset.getConfig(['elm.json'], {parse: false});
  if (!elmJson) {
    createElmJson(pathToElmBin);
    // Watch the new elm.json for changes
    await asset.getConfig(['elm.json'], {parse: false});
  }

Review comment (bug):
This would break with Yarn PnP, it should be something like this:
```js
async function ensureElmInstalled(asset, options) {
  if (!commandExists.sync('elm')) {
    await options.packageManager.resolve('elm/package.json', asset.filePath, {
      autoinstall: options.autoinstall,
    });
  }
}
```
and then you spawn `elm` while relying on npm/npx/Yarn to have injected the bin folder into the PATH.


I'm also still getting errors from `@parcel/package-manager` (`@parcel/transformer-elm: Cannot find module 'elm' from '.../elm/src'`), there is probably still some code where the `/package.json` part of `elm/package.json` is removed incorrectly.

Code after:
            "Initialize your elm project by running 'elm init'",
            "If you installed elm as project dependency then run 'yarn elm init' or 'npx elm init'",
          ],
        },
      });
    }
    config.setResult(elmConfig.contents);
  },

  async transform({asset, options}) {
    const elmBinary = await elmBinaryPath(asset.filePath, options);
    const elm = await options.packageManager.require(
      'node-elm-compiler',
      asset.filePath,
      {
        autoinstall: options.autoinstall,
        saveDev: true,
      },
    );

    const compilerConfig = {
      spawn,
      cwd: path.dirname(asset.filePath),
      // $FlowFixMe[sketchy-null-string]
      debug: !options.env.PARCEL_ELM_NO_DEBUG && options.mode !== 'production',
      optimize: asset.env.minify,
    };
    asset.invalidateOnEnvChange('PARCEL_ELM_NO_DEBUG');
    for (const filePath of await elm.findAllDependencies(asset.filePath)) {
      asset.addIncludedFile(filePath);
    }

    // Workaround for `chdir` not working in workers
    // this can be removed after https://github.com/isaacs/node-graceful-fs/pull/200 was mergend and used in parcel
    process.chdir.disabled = isWorker;

    let code = await compileToString(elm, elmBinary, asset, compilerConfig);
    if (options.hot) {
      code = await injectHotModuleReloadRuntime(code, asset.filePath, options);
    }
    if (compilerConfig.optimize) code = await minifyElmOutput(code);

    asset.type = 'js';
    asset.setCode(code);
    return [asset];
  },
}): Transformer);

async function elmBinaryPath(searchPath, options) {
  let elmBinary = await resolveLocalElmBinary(searchPath, options);