From 19bcacaccc14df05bfc480bbc752aa4e33914e69 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 8 Mar 2023 06:47:50 +0100 Subject: [PATCH] fix(turbopack): Update app-renderer (vercel/turbo#4102) ### Description This PR mirrors some related manifest changes in Next.js: https://github.com/vercel/next.js/pull/46881. ### Testing Instructions --------- Co-authored-by: Justin Ridgewell --- crates/next-core/js/package.json | 2 +- .../next-core/js/src/entry/app-renderer.tsx | 44 +- .../Issue while running loader-00caad.txt | 21 + .../Issue while running loader-9bd07c.txt | 21 - .../Issue while running loader-bb7ea7.txt | 21 - .../Issue while running loader-c98676.txt | 21 + crates/next-dev-tests/tests/package.json | 2 +- .../benches/bundlers/turbopack/mod.rs | 2 +- crates/turbo-tasks-fs/src/async_file.rs | 853 ++++++++++++++++++ 9 files changed, 924 insertions(+), 63 deletions(-) create mode 100644 crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-00caad.txt delete mode 100644 crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-9bd07c.txt delete mode 100644 crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-bb7ea7.txt create mode 100644 crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-c98676.txt create mode 100644 crates/turbo-tasks-fs/src/async_file.rs diff --git a/crates/next-core/js/package.json b/crates/next-core/js/package.json index 1cad37d1a8ab0..f552142d2101b 100644 --- a/crates/next-core/js/package.json +++ b/crates/next-core/js/package.json @@ -12,7 +12,7 @@ "@vercel/turbopack-runtime": "latest", "anser": "^2.1.1", "css.escape": "^1.5.1", - "next": "13.1.7-canary.30", + "next": "13.2.4-canary.6", "platform": "1.3.6", "react-dom": "^18.2.0", "react": "^18.2.0", diff --git a/crates/next-core/js/src/entry/app-renderer.tsx b/crates/next-core/js/src/entry/app-renderer.tsx index 966ed2605be51..ec135ce31efd0 100644 --- a/crates/next-core/js/src/entry/app-renderer.tsx +++ b/crates/next-core/js/src/entry/app-renderer.tsx @@ -137,32 +137,40 @@ async function runOperation(renderData: RenderData) { tree = [info.segment, { children: tree }, components]; } - const proxyMethodsForModule = ( - id: string, - css: boolean - ): ProxyHandler => ({ - get(target, name, receiver) { - return { - id, - chunks: JSON.parse(id)[1], - name, - }; - }, - }); - const proxyMethods = (css: boolean): ProxyHandler => { + const proxyMethods = (): ProxyHandler => { return { - get(target, name, receiver) { - if (name === "__ssr_module_mapping__") { + get(_target, key: string) { + if (key === "__ssr_module_mapping__") { return manifest; } - if (name === "__entry_css_files__") { + if (key === "__entry_css_files__") { return __entry_css_files__; } - return new Proxy({}, proxyMethodsForModule(name as string, css)); + + // The key is a `${file}#${name}`, but `file` can contain `#` itself. + // There are 3 possibilities: + // "file" => id = "file", name = "*" + // "file#" => id = "file", name = "" + // "file#foo" => id = "file", name = "foo" + const pos = key.lastIndexOf("#"); + let id = key; + let name = ""; + if (pos === -1) { + name = "*"; + } else { + id = key.slice(0, pos); + name = key.slice(pos + 1); + } + + return { + id, + name, + chunks: JSON.parse(id)[1], + }; }, }; }; - const manifest: FlightManifest = new Proxy({} as any, proxyMethods(false)); + const manifest: FlightManifest = new Proxy({} as any, proxyMethods()); const serverCSSManifest: FlightCSSManifest = {}; const __entry_css_files__: FlightManifest["__entry_css_files__"] = {}; for (const [key, chunks] of Object.entries(layoutInfoChunks)) { diff --git a/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-00caad.txt b/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-00caad.txt new file mode 100644 index 0000000000000..c192a236b6089 --- /dev/null +++ b/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-00caad.txt @@ -0,0 +1,21 @@ +PlainIssue { + severity: Error, + context: "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/hello.emit", + category: "loaders", + title: "Issue while running loader", + description: "Error: Error!\n at Object.module.exports (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/node_modules/emit-loader/index.js:4:18)\n at LOADER_EXECUTION (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4134)\n at runSyncOrAsync (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4145)\n at iterateNormalLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5782)\n at (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5426)\n at readResource (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:55:17)\n at (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:6160)\n at processResource (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5308)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4667)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4764)\n at (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4896)\n at handleResult (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:1424)\n at loadLoader (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:963)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4794)\n at runLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:8590)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:35:9)\n at \n at Module.transform (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:28:12)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]__516bc8._.js:13:212)\n at Module.run (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_ipc_evaluate.ts._.js:172:45)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)\n", + detail: "", + documentation_link: "", + source: None, + sub_issues: [], + processing_path: Some( + [ + PlainIssueProcessingPathItem { + context: Some( + "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/index.js", + ), + description: "Next.js pages directory", + }, + ], + ), +} \ No newline at end of file diff --git a/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-9bd07c.txt b/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-9bd07c.txt deleted file mode 100644 index bd8631682017d..0000000000000 --- a/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-9bd07c.txt +++ /dev/null @@ -1,21 +0,0 @@ -PlainIssue { - severity: Warning, - context: "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/hello.emit", - category: "loaders", - title: "Issue while running loader", - description: "Error: Warning!\n at Object.module.exports (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/node_modules/emit-loader/index.js:2:20)\n at LOADER_EXECUTION (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4134)\n at runSyncOrAsync (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4145)\n at iterateNormalLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5782)\n at (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5426)\n at readResource (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:55:17)\n at (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:6160)\n at processResource (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5308)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4667)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4764)\n at (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4896)\n at handleResult (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:1424)\n at loadLoader (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:963)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4794)\n at runLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:8590)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:35:9)\n at \n at Module.transform (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:28:12)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]__516bc8._.js:13:212)\n at Module.run (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_ipc_evaluate.ts._.js:172:45)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)\n", - detail: "", - documentation_link: "", - source: None, - sub_issues: [], - processing_path: Some( - [ - PlainIssueProcessingPathItem { - context: Some( - "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/index.js", - ), - description: "Next.js pages directory", - }, - ], - ), -} \ No newline at end of file diff --git a/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-bb7ea7.txt b/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-bb7ea7.txt deleted file mode 100644 index a3f786e0f9334..0000000000000 --- a/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-bb7ea7.txt +++ /dev/null @@ -1,21 +0,0 @@ -PlainIssue { - severity: Error, - context: "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/hello.emit", - category: "loaders", - title: "Issue while running loader", - description: "Error: Error!\n at Object.module.exports (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/node_modules/emit-loader/index.js:4:18)\n at LOADER_EXECUTION (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4134)\n at runSyncOrAsync (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4145)\n at iterateNormalLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5782)\n at (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5426)\n at readResource (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:55:17)\n at (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:6160)\n at processResource (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5308)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4667)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4764)\n at (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4896)\n at handleResult (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:1424)\n at loadLoader (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:963)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4794)\n at runLoaders (node_modules/.pnpm/next@13.1.7-canary.28_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:8590)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:35:9)\n at \n at Module.transform (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:28:12)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]__516bc8._.js:13:212)\n at Module.run (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_ipc_evaluate.ts._.js:172:45)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)\n", - detail: "", - documentation_link: "", - source: None, - sub_issues: [], - processing_path: Some( - [ - PlainIssueProcessingPathItem { - context: Some( - "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/index.js", - ), - description: "Next.js pages directory", - }, - ], - ), -} \ No newline at end of file diff --git a/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-c98676.txt b/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-c98676.txt new file mode 100644 index 0000000000000..157ae7a7f7f36 --- /dev/null +++ b/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/issues/Issue while running loader-c98676.txt @@ -0,0 +1,21 @@ +PlainIssue { + severity: Warning, + context: "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/hello.emit", + category: "loaders", + title: "Issue while running loader", + description: "Error: Warning!\n at Object.module.exports (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/node_modules/emit-loader/index.js:2:20)\n at LOADER_EXECUTION (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4134)\n at runSyncOrAsync (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4145)\n at iterateNormalLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5782)\n at (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5426)\n at readResource (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:55:17)\n at (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:6160)\n at processResource (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:5308)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4667)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4764)\n at (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4896)\n at handleResult (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:1424)\n at loadLoader (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:963)\n at iteratePitchingLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:4794)\n at runLoaders (node_modules/.pnpm/next@13.2.4-canary.6_pjwopsidmaokadturxaafygjp4/node_modules/next/dist/compiled/loader-runner/LoaderRunner.js:1:8590)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:35:9)\n at \n at Module.transform (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_transforms_webpack-loaders.ts._.js:28:12)\n at (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]__516bc8._.js:13:212)\n at Module.run (crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/.next/build/webpack_loaders/chunks/[turbopack-node]_ipc_evaluate.ts._.js:172:45)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)\n", + detail: "", + documentation_link: "", + source: None, + sub_issues: [], + processing_path: Some( + [ + PlainIssueProcessingPathItem { + context: Some( + "[project]/crates/next-dev-tests/tests/integration/next/webpack-loaders/emitted-errors/input/pages/index.js", + ), + description: "Next.js pages directory", + }, + ], + ), +} \ No newline at end of file diff --git a/crates/next-dev-tests/tests/package.json b/crates/next-dev-tests/tests/package.json index d0530c32f9ea3..140a0dca96f02 100644 --- a/crates/next-dev-tests/tests/package.json +++ b/crates/next-dev-tests/tests/package.json @@ -9,7 +9,7 @@ "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", "loader-runner": "^4.3.0", - "next": "13.1.7-canary.28", + "next": "13.2.4-canary.6", "postcss": "^8.4.20", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/crates/next-dev/benches/bundlers/turbopack/mod.rs b/crates/next-dev/benches/bundlers/turbopack/mod.rs index 2ff754de987f0..a5fd6155d6c9d 100644 --- a/crates/next-dev/benches/bundlers/turbopack/mod.rs +++ b/crates/next-dev/benches/bundlers/turbopack/mod.rs @@ -49,7 +49,7 @@ impl Bundler for Turbopack { npm::install( install_dir, &[ - NpmPackage::new("next", "13.1.7-canary.28"), + NpmPackage::new("next", "13.2.4-canary.6"), // Dependency on this is inserted by swc's preset_env NpmPackage::new("@swc/helpers", "^0.4.11"), ], diff --git a/crates/turbo-tasks-fs/src/async_file.rs b/crates/turbo-tasks-fs/src/async_file.rs new file mode 100644 index 0000000000000..cc24a299f1365 --- /dev/null +++ b/crates/turbo-tasks-fs/src/async_file.rs @@ -0,0 +1,853 @@ +//! Types for working with [`File`]. +//! +//! [`File`]: File + +pub(crate) const MAX_BUF: usize = 2 * 1024 * 1024; + +use std::{ + cmp, fmt, + fs::{File as StdFile, Metadata, Permissions}, + future::Future, + io::{self, Read, Seek, SeekFrom, Write}, + path::Path, + pin::Pin, + sync::Arc, + task::{Context, Poll, Poll::*}, +}; + +use tokio::{ + io::{AsyncRead, AsyncSeek, ReadBuf}, + sync::Mutex, + task::{spawn_blocking, JoinHandle}, +}; + +pub(crate) async fn asyncify(f: F) -> io::Result +where + F: FnOnce() -> io::Result + Send + 'static, + T: Send + 'static, +{ + match spawn_blocking(f).await { + Ok(res) => res, + Err(_) => Err(io::Error::new( + io::ErrorKind::Other, + "background task failed", + )), + } +} + +#[derive(Debug)] +pub(crate) struct Buf { + buf: Vec, + pos: usize, +} + +/// Repeats operations that are interrupted. +macro_rules! uninterruptibly { + ($e:expr) => {{ + loop { + match $e { + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + res => break res, + } + } + }}; +} +macro_rules! ready { + ($e:expr $(,)?) => { + match $e { + std::task::Poll::Ready(t) => t, + std::task::Poll::Pending => return std::task::Poll::Pending, + } + }; +} + +impl Buf { + pub(crate) fn with_capacity(n: usize) -> Buf { + Buf { + buf: Vec::with_capacity(n), + pos: 0, + } + } + + pub(crate) fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub(crate) fn len(&self) -> usize { + self.buf.len() - self.pos + } + + pub(crate) fn copy_to(&mut self, dst: &mut ReadBuf<'_>) -> usize { + let n = cmp::min(self.len(), dst.remaining()); + dst.put_slice(&self.bytes()[..n]); + self.pos += n; + + if self.pos == self.buf.len() { + self.buf.truncate(0); + self.pos = 0; + } + + n + } + + pub(crate) fn copy_from(&mut self, src: &[u8]) -> usize { + assert!(self.is_empty()); + + let n = cmp::min(src.len(), MAX_BUF); + + self.buf.extend_from_slice(&src[..n]); + n + } + + pub(crate) fn bytes(&self) -> &[u8] { + &self.buf[self.pos..] + } + + pub(crate) fn ensure_capacity_for(&mut self, bytes: &ReadBuf<'_>) { + assert!(self.is_empty()); + + let len = cmp::min(bytes.remaining(), MAX_BUF); + + if self.buf.len() < len { + self.buf.reserve(len - self.buf.len()); + } + + unsafe { + self.buf.set_len(len); + } + } + + pub(crate) fn read_from(&mut self, rd: &mut T) -> io::Result { + let res = uninterruptibly!(rd.read(&mut self.buf)); + + if let Ok(n) = res { + self.buf.truncate(n); + } else { + self.buf.clear(); + } + + assert_eq!(self.pos, 0); + + res + } + + pub(crate) fn write_to(&mut self, wr: &mut T) -> io::Result<()> { + assert_eq!(self.pos, 0); + + // `write_all` already ignores interrupts + let res = wr.write_all(&self.buf); + self.buf.clear(); + res + } + + pub(crate) fn discard_read(&mut self) -> i64 { + let ret = -(self.bytes().len() as i64); + self.pos = 0; + self.buf.truncate(0); + ret + } +} + +use self::State::*; + +/// A reference to an open file on the filesystem. +/// +/// This is a specialized version of [`std::fs::File`][std] for usage from the +/// Tokio runtime. +/// +/// An instance of a `File` can be read and/or written depending on what options +/// it was opened with. Files also implement [`AsyncSeek`] to alter the logical +/// cursor that the file contains internally. +/// +/// A file will not be closed immediately when it goes out of scope if there +/// are any IO operations that have not yet completed. To ensure that a file is +/// closed immediately when it is dropped, you should call [`flush`] before +/// dropping it. Note that this does not ensure that the file has been fully +/// written to disk; the operating system might keep the changes around in an +/// in-memory buffer. See the [`sync_all`] method for telling the OS to write +/// the data to disk. +/// +/// Reading and writing to a `File` is usually done using the convenience +/// methods found on the [`AsyncReadExt`] and [`AsyncWriteExt`] traits. +/// +/// [std]: struct@std::fs::File +/// [`AsyncSeek`]: trait@crate::io::AsyncSeek +/// [`flush`]: fn@crate::io::AsyncWriteExt::flush +/// [`sync_all`]: fn@crate::fs::File::sync_all +/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt +/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt +/// +/// # Examples +/// +/// Create a new file and asynchronously write bytes to it: +/// +/// ```no_run +/// use tokio::fs::File; +/// use tokio::io::AsyncWriteExt; // for write_all() +/// +/// # async fn dox() -> std::io::Result<()> { +/// let mut file = File::create("foo.txt").await?; +/// file.write_all(b"hello, world!").await?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Read the contents of a file into a buffer: +/// +/// ```no_run +/// use tokio::fs::File; +/// use tokio::io::AsyncReadExt; // for read_to_end() +/// +/// # async fn dox() -> std::io::Result<()> { +/// let mut file = File::open("foo.txt").await?; +/// +/// let mut contents = vec![]; +/// file.read_to_end(&mut contents).await?; +/// +/// println!("len = {}", contents.len()); +/// # Ok(()) +/// # } +/// ``` +pub struct File { + std: Arc, + inner: Mutex, +} + +struct Inner { + state: State, + + /// Errors from writes/flushes are returned in write/flush calls. If a write + /// error is observed while performing a read, it is saved until the next + /// write / flush call. + last_write_err: Option, + + pos: u64, +} + +#[derive(Debug)] +enum State { + Idle(Option), + Busy(JoinHandle<(Operation, Buf)>), +} + +#[derive(Debug)] +enum Operation { + Read(io::Result), + Write(io::Result<()>), + Seek(io::Result), +} + +impl File { + /// Attempts to open a file in read-only mode. + /// + /// See [`OpenOptions`] for more details. + /// + /// [`OpenOptions`]: super::OpenOptions + /// + /// # Errors + /// + /// This function will return an error if called from outside of the Tokio + /// runtime or if path does not already exist. Other errors may also be + /// returned according to OpenOptions::open. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// use tokio::io::AsyncReadExt; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt").await?; + /// + /// let mut contents = vec![]; + /// file.read_to_end(&mut contents).await?; + /// + /// println!("len = {}", contents.len()); + /// # Ok(()) + /// # } + /// ``` + /// + /// The [`read_to_end`] method is defined on the [`AsyncReadExt`] trait. + /// + /// [`read_to_end`]: fn@crate::io::AsyncReadExt::read_to_end + /// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt + pub async fn open(path: impl AsRef) -> io::Result { + let path = path.as_ref().to_owned(); + let std = asyncify(|| StdFile::open(path)).await?; + + Ok(File::from_std(std)) + } + + /// Opens a file in write-only mode. + /// + /// This function will create a file if it does not exist, and will truncate + /// it if it does. + /// + /// See [`OpenOptions`] for more details. + /// + /// [`OpenOptions`]: super::OpenOptions + /// + /// # Errors + /// + /// Results in an error if called from outside of the Tokio runtime or if + /// the underlying [`create`] call results in an error. + /// + /// [`create`]: std::fs::File::create + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// use tokio::io::AsyncWriteExt; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. + /// + /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all + /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt + pub async fn create(path: impl AsRef) -> io::Result { + let path = path.as_ref().to_owned(); + let std_file = asyncify(move || StdFile::create(path)).await?; + Ok(File::from_std(std_file)) + } + + /// Converts a [`std::fs::File`][std] to a [`tokio::fs::File`][file]. + /// + /// [std]: std::fs::File + /// [file]: File + /// + /// # Examples + /// + /// ```no_run + /// // This line could block. It is not recommended to do this on the Tokio + /// // runtime. + /// let std_file = std::fs::File::open("foo.txt").unwrap(); + /// let file = tokio::fs::File::from_std(std_file); + /// ``` + pub fn from_std(std: StdFile) -> File { + File { + std: Arc::new(std), + inner: Mutex::new(Inner { + state: State::Idle(Some(Buf::with_capacity(0))), + last_write_err: None, + pos: 0, + }), + } + } + + /// Attempts to sync all OS-internal metadata to disk. + /// + /// This function will attempt to ensure that all in-core data reaches the + /// filesystem before returning. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// use tokio::io::AsyncWriteExt; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// file.sync_all().await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. + /// + /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all + /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt + pub async fn sync_all(&self) -> io::Result<()> { + let mut inner = self.inner.lock().await; + inner.complete_inflight().await; + + let std = self.std.clone(); + asyncify(move || std.sync_all()).await + } + + /// This function is similar to `sync_all`, except that it may not + /// synchronize file metadata to the filesystem. + /// + /// This is intended for use cases that must synchronize content, but don't + /// need the metadata on disk. The goal of this method is to reduce disk + /// operations. + /// + /// Note that some platforms may simply implement this in terms of + /// `sync_all`. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// use tokio::io::AsyncWriteExt; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// file.sync_data().await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. + /// + /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all + /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt + pub async fn sync_data(&self) -> io::Result<()> { + let mut inner = self.inner.lock().await; + inner.complete_inflight().await; + + let std = self.std.clone(); + asyncify(move || std.sync_data()).await + } + + /// Truncates or extends the underlying file, updating the size of this file + /// to become size. + /// + /// If the size is less than the current file's size, then the file will be + /// shrunk. If it is greater than the current file's size, then the file + /// will be extended to size and have all of the intermediate data filled in + /// with 0s. + /// + /// # Errors + /// + /// This function will return an error if the file is not opened for + /// writing. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// use tokio::io::AsyncWriteExt; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let mut file = File::create("foo.txt").await?; + /// file.write_all(b"hello, world!").await?; + /// file.set_len(10).await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. + /// + /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all + /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt + pub async fn set_len(&self, size: u64) -> io::Result<()> { + let mut inner = self.inner.lock().await; + inner.complete_inflight().await; + + let mut buf = match inner.state { + Idle(ref mut buf_cell) => buf_cell.take().unwrap(), + _ => unreachable!(), + }; + + let seek = if !buf.is_empty() { + Some(SeekFrom::Current(buf.discard_read())) + } else { + None + }; + + let std = self.std.clone(); + + inner.state = Busy(spawn_blocking(move || { + let res = if let Some(seek) = seek { + (&*std).seek(seek).and_then(|_| std.set_len(size)) + } else { + std.set_len(size) + } + .map(|_| 0); // the value is discarded later + + // Return the result as a seek + (Operation::Seek(res), buf) + })); + + let (op, buf) = match inner.state { + Idle(_) => unreachable!(), + Busy(ref mut rx) => rx.await?, + }; + + inner.state = Idle(Some(buf)); + + match op { + Operation::Seek(res) => res.map(|pos| { + inner.pos = pos; + }), + _ => unreachable!(), + } + } + + /// Queries metadata about the underlying file. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::open("foo.txt").await?; + /// let metadata = file.metadata().await?; + /// + /// println!("{:?}", metadata); + /// # Ok(()) + /// # } + /// ``` + pub async fn metadata(&self) -> io::Result { + let std = self.std.clone(); + asyncify(move || std.metadata()).await + } + + /// Creates a new `File` instance that shares the same underlying file + /// handle as the existing `File` instance. Reads, writes, and seeks + /// will affect both File instances simultaneously. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::open("foo.txt").await?; + /// let file_clone = file.try_clone().await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn try_clone(&self) -> io::Result { + let std = self.std.clone(); + let std_file = asyncify(move || std.try_clone()).await?; + Ok(File::from_std(std_file)) + } + + /// Destructures `File` into a [`std::fs::File`][std]. This function is + /// async to allow any in-flight operations to complete. + /// + /// Use `File::try_into_std` to attempt conversion immediately. + /// + /// [std]: std::fs::File + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let tokio_file = File::open("foo.txt").await?; + /// let std_file = tokio_file.into_std().await; + /// # Ok(()) + /// # } + /// ``` + pub async fn into_std(mut self) -> StdFile { + self.inner.get_mut().complete_inflight().await; + Arc::try_unwrap(self.std).expect("Arc::try_unwrap failed") + } + + /// Tries to immediately destructure `File` into a [`std::fs::File`][std]. + /// + /// [std]: std::fs::File + /// + /// # Errors + /// + /// This function will return an error containing the file if some + /// operation is in-flight. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let tokio_file = File::open("foo.txt").await?; + /// let std_file = tokio_file.try_into_std().unwrap(); + /// # Ok(()) + /// # } + /// ``` + pub fn try_into_std(mut self) -> Result { + match Arc::try_unwrap(self.std) { + Ok(file) => Ok(file), + Err(std_file_arc) => { + self.std = std_file_arc; + Err(self) + } + } + } + + /// Changes the permissions on the underlying file. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `fchmod` function on Unix and + /// the `SetFileInformationByHandle` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior + /// + /// # Errors + /// + /// This function will return an error if the user lacks permission change + /// attributes on the underlying file. It may also return an error in other + /// os-specific unspecified cases. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::fs::File; + /// + /// # async fn dox() -> std::io::Result<()> { + /// let file = File::open("foo.txt").await?; + /// let mut perms = file.metadata().await?.permissions(); + /// perms.set_readonly(true); + /// file.set_permissions(perms).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + let std = self.std.clone(); + asyncify(move || std.set_permissions(perm)).await + } +} + +impl AsyncRead for File { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + dst: &mut ReadBuf<'_>, + ) -> Poll> { + let me = self.get_mut(); + let inner = me.inner.get_mut(); + + loop { + match inner.state { + Idle(ref mut buf_cell) => { + let mut buf = buf_cell.take().unwrap(); + + if !buf.is_empty() { + buf.copy_to(dst); + *buf_cell = Some(buf); + return Ready(Ok(())); + } + + buf.ensure_capacity_for(dst); + let std = me.std.clone(); + + inner.state = Busy(spawn_blocking(move || { + let res = buf.read_from(&mut &*std); + (Operation::Read(res), buf) + })); + } + Busy(ref mut rx) => { + let (op, mut buf) = ready!(Pin::new(rx).poll(cx))?; + + match op { + Operation::Read(Ok(_)) => { + buf.copy_to(dst); + inner.state = Idle(Some(buf)); + return Ready(Ok(())); + } + Operation::Read(Err(e)) => { + assert!(buf.is_empty()); + + inner.state = Idle(Some(buf)); + return Ready(Err(e)); + } + Operation::Write(Ok(_)) => { + assert!(buf.is_empty()); + inner.state = Idle(Some(buf)); + continue; + } + Operation::Write(Err(e)) => { + assert!(inner.last_write_err.is_none()); + inner.last_write_err = Some(e.kind()); + inner.state = Idle(Some(buf)); + } + Operation::Seek(result) => { + assert!(buf.is_empty()); + inner.state = Idle(Some(buf)); + if let Ok(pos) = result { + inner.pos = pos; + } + continue; + } + } + } + } + } + } +} + +impl AsyncSeek for File { + fn start_seek(self: Pin<&mut Self>, mut pos: SeekFrom) -> io::Result<()> { + let me = self.get_mut(); + let inner = me.inner.get_mut(); + + match inner.state { + Busy(_) => Err(io::Error::new( + io::ErrorKind::Other, + "other file operation is pending, call poll_complete before start_seek", + )), + Idle(ref mut buf_cell) => { + let mut buf = buf_cell.take().unwrap(); + + // Factor in any unread data from the buf + if !buf.is_empty() { + let n = buf.discard_read(); + + if let SeekFrom::Current(ref mut offset) = pos { + *offset += n; + } + } + + let std = me.std.clone(); + + inner.state = Busy(spawn_blocking(move || { + let res = (&*std).seek(pos); + (Operation::Seek(res), buf) + })); + Ok(()) + } + } + } + + fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let inner = self.inner.get_mut(); + + loop { + match inner.state { + Idle(_) => return Poll::Ready(Ok(inner.pos)), + Busy(ref mut rx) => { + let (op, buf) = ready!(Pin::new(rx).poll(cx))?; + inner.state = Idle(Some(buf)); + + match op { + Operation::Read(_) => {} + Operation::Write(Err(e)) => { + assert!(inner.last_write_err.is_none()); + inner.last_write_err = Some(e.kind()); + } + Operation::Write(_) => {} + Operation::Seek(res) => { + if let Ok(pos) = res { + inner.pos = pos; + } + return Ready(res); + } + } + } + } + } + } +} + +impl From for File { + fn from(std: StdFile) -> Self { + Self::from_std(std) + } +} + +impl fmt::Debug for File { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("tokio::fs::File") + .field("std", &self.std) + .finish() + } +} + +#[cfg(unix)] +impl std::os::unix::io::AsRawFd for File { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + self.std.as_raw_fd() + } +} + +#[cfg(unix)] +impl std::os::unix::io::FromRawFd for File { + unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self { + StdFile::from_raw_fd(fd).into() + } +} + +#[cfg(windows)] +impl std::os::windows::io::AsRawHandle for File { + fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { + self.std.as_raw_handle() + } +} + +#[cfg(windows)] +impl std::os::windows::io::FromRawHandle for File { + unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self { + StdFile::from_raw_handle(handle).into() + } +} + +pub struct PollFn { + f: F, +} + +/// Creates a new future wrapping around a function returning [`Poll`]. +pub fn poll_fn(f: F) -> PollFn +where + F: FnMut(&mut Context<'_>) -> Poll, +{ + PollFn { f } +} + +impl fmt::Debug for PollFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PollFn").finish() + } +} + +impl Future for PollFn +where + F: FnMut(&mut Context<'_>) -> Poll, +{ + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // Safety: We never construct a `Pin<&mut F>` anywhere, so accessing `f` + // mutably in an unpinned way is sound. + // + // This use of unsafe cannot be replaced with the pin-project macro + // because: + // * If we put `#[pin]` on the field, then it gives us a `Pin<&mut F>`, which + // we can't use to call the closure. + // * If we don't put `#[pin]` on the field, then it makes `PollFn` be + // unconditionally `Unpin`, which we also don't want. + let me = unsafe { Pin::into_inner_unchecked(self) }; + (me.f)(cx) + } +} + +impl Inner { + async fn complete_inflight(&mut self) { + if let Err(e) = poll_fn(|cx| Pin::new(&mut *self).poll_flush(cx)).await { + self.last_write_err = Some(e.kind()); + } + } + + fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll> { + if let Some(e) = self.last_write_err.take() { + return Ready(Err(e.into())); + } + + let (op, buf) = match self.state { + Idle(_) => return Ready(Ok(())), + Busy(ref mut rx) => ready!(Pin::new(rx).poll(cx))?, + }; + + // The buffer is not used here + self.state = Idle(Some(buf)); + + match op { + Operation::Read(_) => Ready(Ok(())), + Operation::Write(res) => Ready(res), + Operation::Seek(_) => Ready(Ok(())), + } + } +}