{"version":3,"sources":["../../../../src/build/webpack/loaders/next-barrel-loader.ts"],"sourcesContent":["/**\n * ## Barrel Optimizations\n *\n * This loader is used to optimize the imports of \"barrel\" files that have many\n * re-exports. Currently, both Node.js and Webpack have to enter all of these\n * submodules even if we only need a few of them.\n *\n * For example, say a file `foo.js` with the following contents:\n *\n *   export { a } from './a'\n *   export { b } from './b'\n *   export { c } from './c'\n *   ...\n *\n * If the user imports `a` only, this loader will accept the `names` option to\n * be `['a']`. Then, it request the \"__barrel_transform__\" SWC transform to load\n * `foo.js` and receive the following output:\n *\n *   export const __next_private_export_map__ = '[[\"a\",\"./a\",\"a\"],[\"b\",\"./b\",\"b\"],[\"c\",\"./c\",\"c\"],...]'\n *\n *   format: '[\"<imported identifier>\", \"<import path>\", \"<exported name>\"]'\n *   e.g.: import { a as b } from './module-a' => '[\"b\", \"./module-a\", \"a\"]'\n *\n * The export map, generated by SWC, is a JSON that represents the exports of\n * that module, their original file, and their original name (since you can do\n * `export { a as b }`).\n *\n * Then, this loader can safely remove all the exports that are not needed and\n * re-export the ones from `names`:\n *\n *   export { a } from './a'\n *\n * That's the basic situation and also the happy path.\n *\n *\n *\n * ## Wildcard Exports\n *\n * For wildcard exports (e.g. `export * from './a'`), it becomes a bit more complicated.\n * Say `foo.js` with the following contents:\n *\n *   export * from './a'\n *   export * from './b'\n *   export * from './c'\n *   ...\n *\n * If the user imports `bar` from it, SWC can never know which files are going to be\n * exporting `bar`. So, we have to keep all the wildcard exports and do the same\n * process recursively. This loader will return the following output:\n *\n *   export * from '__barrel_optimize__?names=bar&wildcard!=!./a'\n *   export * from '__barrel_optimize__?names=bar&wildcard!=!./b'\n *   export * from '__barrel_optimize__?names=bar&wildcard!=!./c'\n *   ...\n *\n * The \"!=!\" tells Webpack to use the same loader to process './a', './b', and './c'.\n * After the recursive process, the \"inner loaders\" will either return an empty string\n * or:\n *\n *   export * from './target'\n *\n * Where `target` is the file that exports `bar`.\n *\n *\n *\n * ## Non-Barrel Files\n *\n * If the file is not a barrel, we can't apply any optimizations. That's because\n * we can't easily remove things from the file. For example, say `foo.js` with:\n *\n *   const v = 1\n *   export function b () {\n *     return v\n *   }\n *\n * If the user imports `b` only, we can't remove the `const v = 1` even though\n * the file is side-effect free. In these caes, this loader will simply re-export\n * `foo.js`:\n *\n *   export * from './foo'\n *\n * Besides these cases, this loader also carefully handles the module cache so\n * SWC won't analyze the same file twice, and no instance of the same file will\n * be accidentally created as different instances.\n */\n\nimport type webpack from 'webpack'\n\nimport path from 'path'\nimport { transform } from '../../swc'\nimport { installBindings } from '../../swc/install-bindings'\n\n// This is a in-memory cache for the mapping of barrel exports. This only applies\n// to the packages that we optimize. It will never change (e.g. upgrading packages)\n// during the lifetime of the server so we can safely cache it.\n// There is also no need to collect the cache for the same reason.\nconst barrelTransformMappingCache = new Map<\n  string,\n  {\n    exportList: [string, string, string][]\n    wildcardExports: string[]\n    isClientEntry: boolean\n  } | null\n>()\n\nasync function getBarrelMapping(\n  resourcePath: string,\n  swcCacheDir: string,\n  resolve: (context: string, request: string) => Promise<string>,\n  fs: {\n    readFile: (\n      path: string,\n      callback: (err: any, data: string | Buffer | undefined) => void\n    ) => void\n  }\n) {\n  if (barrelTransformMappingCache.has(resourcePath)) {\n    return barrelTransformMappingCache.get(resourcePath)!\n  }\n\n  // This is a SWC transform specifically for `optimizeBarrelExports`. We don't\n  // care about other things but the export map only.\n  async function transpileSource(\n    filename: string,\n    source: string,\n    isWildcard: boolean\n  ) {\n    const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx')\n    return new Promise<string>((res) =>\n      transform(source, {\n        filename,\n        inputSourceMap: undefined,\n        sourceFileName: filename,\n        optimizeBarrelExports: {\n          wildcard: isWildcard,\n        },\n        jsc: {\n          parser: {\n            syntax: isTypeScript ? 'typescript' : 'ecmascript',\n            [isTypeScript ? 'tsx' : 'jsx']: true,\n          },\n          experimental: {\n            cacheRoot: swcCacheDir,\n          },\n        },\n      }).then((output) => {\n        res(output.code)\n      })\n    )\n  }\n\n  // Avoid circular `export *` dependencies\n  const visited = new Set<string>()\n  async function getMatches(\n    file: string,\n    isWildcard: boolean,\n    isClientEntry: boolean\n  ) {\n    if (visited.has(file)) {\n      return null\n    }\n    visited.add(file)\n\n    const source = await new Promise<string>((res, rej) => {\n      fs.readFile(file, (err, data) => {\n        if (err || data === undefined) {\n          rej(err)\n        } else {\n          res(data.toString())\n        }\n      })\n    })\n\n    const output = await transpileSource(file, source, isWildcard)\n\n    const matches = output.match(\n      /^([^]*)export (const|var) __next_private_export_map__ = ('[^']+'|\"[^\"]+\")/\n    )\n    if (!matches) {\n      return null\n    }\n\n    const matchedDirectives = output.match(\n      /^([^]*)export (const|var) __next_private_directive_list__ = '([^']+)'/\n    )\n    const directiveList = matchedDirectives\n      ? JSON.parse(matchedDirectives[3])\n      : []\n    // \"use client\" in barrel files has to be transferred to the target file.\n    isClientEntry = directiveList.includes('use client')\n\n    let exportList = JSON.parse(matches[3].slice(1, -1)) as [\n      string,\n      string,\n      string,\n    ][]\n    const wildcardExports = [\n      ...output.matchAll(/export \\* from \"([^\"]+)\"/g),\n    ].map((match) => match[1])\n\n    // In the wildcard case, if the value is exported from another file, we\n    // redirect to that file (decl[0]). Otherwise, export from the current\n    // file itself.\n    if (isWildcard) {\n      for (const decl of exportList) {\n        decl[1] = file\n        decl[2] = decl[0]\n      }\n    }\n\n    // This recursively handles the wildcard exports (e.g. `export * from './a'`)\n    if (wildcardExports.length) {\n      await Promise.all(\n        wildcardExports.map(async (req) => {\n          const targetPath = await resolve(\n            path.dirname(file),\n            req.replace('__barrel_optimize__?names=__PLACEHOLDER__!=!', '')\n          )\n\n          const targetMatches = await getMatches(\n            targetPath,\n            true,\n            isClientEntry\n          )\n\n          if (targetMatches) {\n            // Merge the export list\n            exportList = exportList.concat(targetMatches.exportList)\n          }\n        })\n      )\n    }\n\n    return {\n      exportList,\n      wildcardExports,\n      isClientEntry,\n    }\n  }\n\n  const res = await getMatches(resourcePath, false, false)\n  barrelTransformMappingCache.set(resourcePath, res)\n\n  return res\n}\n\nconst NextBarrelLoader = async function (\n  this: webpack.LoaderContext<{\n    names: string[]\n    swcCacheDir: string\n  }>\n) {\n  this.async()\n  this.cacheable(true)\n  // Install bindings early so they are definitely available.\n  // When run by webpack in next this is already done with correct configuration so this is a no-op.\n  // In turbopack loaders are run in a subprocess so it may or may not be done.\n  await installBindings()\n\n  const { names, swcCacheDir } = this.getOptions()\n\n  // For barrel optimizations, we always prefer the \"module\" field over the\n  // \"main\" field because ESM handling is more robust with better tree-shaking.\n  const resolve = this.getResolve({\n    mainFields: ['module', 'main'],\n  })\n\n  const mapping = await getBarrelMapping(\n    this.resourcePath,\n    swcCacheDir,\n    resolve,\n    this.fs\n  )\n\n  // `resolve` adds all sub-paths to the dependency graph. However, we already\n  // cached the mapping and we assume them to not change. So, we can safely\n  // clear the dependencies here to avoid unnecessary watchers which turned out\n  // to be very expensive.\n  this.clearDependencies()\n\n  if (!mapping) {\n    // This file isn't a barrel and we can't apply any optimizations. Let's re-export everything.\n    // Since this loader accepts `names` and the request is keyed with `names`, we can't simply\n    // return the original source here. That will create these imports with different names as\n    // different modules instances.\n    this.callback(null, `export * from ${JSON.stringify(this.resourcePath)}`)\n    return\n  }\n\n  const exportList = mapping.exportList\n  const isClientEntry = mapping.isClientEntry\n  const exportMap = new Map<string, [string, string]>()\n  for (const [name, filePath, orig] of exportList) {\n    exportMap.set(name, [filePath, orig])\n  }\n\n  let output = ''\n  let missedNames: string[] = []\n  for (const name of names) {\n    // If the name matches\n    if (exportMap.has(name)) {\n      const decl = exportMap.get(name)!\n\n      if (decl[1] === '*') {\n        output += `\\nexport * as ${name} from ${JSON.stringify(decl[0])}`\n      } else if (decl[1] === 'default') {\n        output += `\\nexport { default as ${name} } from ${JSON.stringify(\n          decl[0]\n        )}`\n      } else if (decl[1] === name) {\n        output += `\\nexport { ${name} } from ${JSON.stringify(decl[0])}`\n      } else {\n        output += `\\nexport { ${decl[1]} as ${name} } from ${JSON.stringify(\n          decl[0]\n        )}`\n      }\n    } else {\n      missedNames.push(name)\n    }\n  }\n\n  // These are from wildcard exports.\n  if (missedNames.length > 0) {\n    for (const req of mapping.wildcardExports) {\n      output += `\\nexport * from ${JSON.stringify(\n        req.replace('__PLACEHOLDER__', missedNames.join(',') + '&wildcard')\n      )}`\n    }\n  }\n\n  // When it has `\"use client\"` inherited from its barrel files, we need to\n  // prefix it to this target file as well.\n  if (isClientEntry) {\n    output = `\"use client\";\\n${output}`\n  }\n\n  this.callback(null, output)\n}\n\nexport default NextBarrelLoader\n"],"names":["barrelTransformMappingCache","Map","getBarrelMapping","resourcePath","swcCacheDir","resolve","fs","has","get","transpileSource","filename","source","isWildcard","isTypeScript","endsWith","Promise","res","transform","inputSourceMap","undefined","sourceFileName","optimizeBarrelExports","wildcard","jsc","parser","syntax","experimental","cacheRoot","then","output","code","visited","Set","getMatches","file","isClientEntry","add","rej","readFile","err","data","toString","matches","match","matchedDirectives","directiveList","JSON","parse","includes","exportList","slice","wildcardExports","matchAll","map","decl","length","all","req","targetPath","path","dirname","replace","targetMatches","concat","set","NextBarrelLoader","async","cacheable","installBindings","names","getOptions","getResolve","mainFields","mapping","clearDependencies","callback","stringify","exportMap","name","filePath","orig","missedNames","push","join"],"mappings":"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","ignoreList":[0]}