getModuleSystem.js 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. const { promises: fsPromises } = require('fs');
  2. const path = require('path');
  3. const commonPathPrefix = require('common-path-prefix');
  4. const findUp = require('find-up');
  5. /** @type {Map<string, string | undefined>} */
  6. let packageJsonTypeMap = new Map();
  7. /**
  8. * Infers the current active module system from loader context and options.
  9. * @this {import('webpack').loader.LoaderContext}
  10. * @param {import('webpack').ModuleFilenameHelpers} ModuleFilenameHelpers Webpack's module filename helpers.
  11. * @param {import('../types').NormalizedLoaderOptions} options The normalized loader options.
  12. * @return {Promise<'esm' | 'cjs'>} The inferred module system.
  13. */
  14. async function getModuleSystem(ModuleFilenameHelpers, options) {
  15. // Check loader options -
  16. // if `esModule` is set we don't have to do extra guess work.
  17. switch (typeof options.esModule) {
  18. case 'boolean': {
  19. return options.esModule ? 'esm' : 'cjs';
  20. }
  21. case 'object': {
  22. if (
  23. options.esModule.include &&
  24. ModuleFilenameHelpers.matchPart(this.resourcePath, options.esModule.include)
  25. ) {
  26. return 'esm';
  27. }
  28. if (
  29. options.esModule.exclude &&
  30. ModuleFilenameHelpers.matchPart(this.resourcePath, options.esModule.exclude)
  31. ) {
  32. return 'cjs';
  33. }
  34. break;
  35. }
  36. default: // Do nothing
  37. }
  38. // Check current resource's extension
  39. if (/\.mjs$/.test(this.resourcePath)) return 'esm';
  40. if (/\.cjs$/.test(this.resourcePath)) return 'cjs';
  41. // Load users' `package.json` -
  42. // We will cache the results in a global variable so it will only be parsed once.
  43. let packageJsonType = packageJsonTypeMap.get(this.rootContext);
  44. if (!packageJsonType) {
  45. try {
  46. const commonPath = commonPathPrefix([this.rootContext, this.resourcePath], '/');
  47. const stopPath = path.resolve(commonPath, '..');
  48. const packageJsonPath = await findUp(
  49. (dir) => {
  50. if (dir === stopPath) return findUp.stop;
  51. return 'package.json';
  52. },
  53. { cwd: path.dirname(this.resourcePath) }
  54. );
  55. const buffer = await fsPromises.readFile(packageJsonPath, { encoding: 'utf-8' });
  56. const rawPackageJson = buffer.toString('utf-8');
  57. ({ type: packageJsonType } = JSON.parse(rawPackageJson));
  58. packageJsonTypeMap.set(this.rootContext, packageJsonType);
  59. } catch (e) {
  60. // Failed to parse `package.json`, do nothing.
  61. }
  62. }
  63. // Check `package.json` for the `type` field -
  64. // fallback to use `cjs` for anything ambiguous.
  65. return packageJsonType === 'module' ? 'esm' : 'cjs';
  66. }
  67. module.exports = getModuleSystem;