Skip to content

Commit

Permalink
fix react import
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Apr 12, 2024
1 parent c600ffb commit e9cbadc
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 11 deletions.
13 changes: 13 additions & 0 deletions docs/lib/react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# React

```js echo
import {createElement} from "react";

display(createElement);
```

```js echo
import {createRoot} from "react-dom/client";

display(createRoot);
```
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@clack/prompts": "^0.7.0",
"@observablehq/inputs": "^0.10.6",
"@observablehq/runtime": "^5.9.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"acorn": "^8.11.2",
"acorn-walk": "^8.3.0",
Expand Down Expand Up @@ -118,6 +119,8 @@
"glob": "^10.3.10",
"mocha": "^10.2.0",
"prettier": "^3.0.3 <3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rimraf": "^5.0.5",
"tempy": "^3.1.0",
"typescript": "^5.2.2",
Expand Down
49 changes: 41 additions & 8 deletions src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import {existsSync} from "node:fs";
import {copyFile, readFile, writeFile} from "node:fs/promises";
import {createRequire} from "node:module";
import op from "node:path";
import {extname, join} from "node:path/posix";
import {dirname, extname, join, relative} from "node:path/posix";
import {pathToFileURL} from "node:url";
import commonjs from "@rollup/plugin-commonjs";
import {nodeResolve} from "@rollup/plugin-node-resolve";
import {packageDirectory} from "pkg-dir";
import type {AstNode, OutputChunk, Plugin, ResolveIdResult} from "rollup";
Expand All @@ -12,7 +13,7 @@ import esbuild from "rollup-plugin-esbuild";
import {prepareOutput, toOsPath} from "./files.js";
import type {ImportReference} from "./javascript/imports.js";
import {isJavaScript, parseImports} from "./javascript/imports.js";
import {parseNpmSpecifier} from "./npm.js";
import {parseNpmSpecifier, rewriteNpmImports} from "./npm.js";
import {isPathImport} from "./path.js";
import {faint} from "./tty.js";

Expand All @@ -38,7 +39,15 @@ async function resolveNodeImportInternal(cacheRoot: string, packageRoot: string,
process.stdout.write(`${spec} ${faint("→")} ${resolution}\n`);
await prepareOutput(outputPath);
if (isJavaScript(pathResolution)) {
await writeFile(outputPath, await bundle(spec, cacheRoot, packageResolution));
await writeFile(
outputPath,
await bundle(
`/_node/${resolution}`,
overrideNodeResolution(spec, packageResolution),
cacheRoot,
packageResolution
)
);
} else {
await copyFile(pathResolution, outputPath);
}
Expand All @@ -51,6 +60,25 @@ async function resolveNodeImportInternal(cacheRoot: string, packageRoot: string,
return `/_node/${resolution}`;
}

/**
* React (and its dependencies) are still distributed as CommonJS modules, which
* means we lose named exports when we bundle them as an ES module — there’s
* only a default export. We can fix this by importing the production bundle
* instead, which is (by luck) compatible with cjs-module-lexer. This is quite
* terrible, and I hope that the React team distributes ES modules soon.
*
* https://github.com/facebook/react/issues/11503
*/
function overrideNodeResolution(specifier: string, packageResolution: string): string {
return specifier === "react"
? op.join(packageResolution, "cjs", "react.production.min.js")
: specifier === "react-dom" || specifier === "react-dom/client"
? op.join(packageResolution, "cjs", "react-dom.production.min.js")
: specifier === "scheduler"
? op.join(packageResolution, "cjs", "scheduler.production.min.js")
: specifier;
}

/**
* Resolves the direct dependencies of the specified node import path, such as
* "/_node/[email protected]/src/index.js", returning a set of node import paths.
Expand All @@ -69,29 +97,34 @@ export function extractNodeSpecifier(path: string): string {
return path.replace(/^\/_node\//, "");
}

async function bundle(input: string, cacheRoot: string, packageRoot: string): Promise<string> {
async function bundle(path: string, input: string, cacheRoot: string, packageRoot: string): Promise<string> {
const bundle = await rollup({
input,
plugins: [
nodeResolve({browser: true, rootDir: packageRoot}),
importResolve(input, cacheRoot, packageRoot),
nodeResolve({browser: true, rootDir: packageRoot}),
commonjs({esmExternals: true}),
esbuild({
format: "esm",
platform: "browser",
target: ["es2022", "chrome96", "firefox96", "safari16", "node18"],
exclude: [], // don’t exclude node_modules
define: {"process.env.NODE_ENV": JSON.stringify("production")},
minify: true
})
],
external(source) {
return source.startsWith("/_node/");
},
onwarn(message, warn) {
if (message.code === "CIRCULAR_DEPENDENCY") return;
warn(message);
}
});
try {
const output = await bundle.generate({format: "es"});
const code = output.output.find((o): o is OutputChunk => o.type === "chunk")!.code; // TODO don’t assume one chunk?
return code;
const output = await bundle.generate({format: "es", exports: "named"});
const code = output.output.find((o): o is OutputChunk => o.type === "chunk")!.code;
return rewriteNpmImports(code, (i) => (i.startsWith("/_node/") ? relative(dirname(path), i) : i));
} finally {
await bundle.close();
}
Expand Down
71 changes: 68 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==

"@jridgewell/sourcemap-codec@^1.4.14":
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
Expand Down Expand Up @@ -410,6 +410,18 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==

"@rollup/plugin-commonjs@^25.0.7":
version "25.0.7"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf"
integrity sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==
dependencies:
"@rollup/pluginutils" "^5.0.1"
commondir "^1.0.1"
estree-walker "^2.0.2"
glob "^8.0.3"
is-reference "1.2.1"
magic-string "^0.30.3"

"@rollup/plugin-node-resolve@^15.2.3":
version "15.2.3"
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9"
Expand Down Expand Up @@ -523,7 +535,7 @@
resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90"
integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==

"@types/[email protected]", "@types/estree@^1.0.0":
"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
Expand Down Expand Up @@ -1128,6 +1140,11 @@ commander@7:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==

commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==

component-emitter@^1.3.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17"
Expand Down Expand Up @@ -1970,7 +1987,7 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"

[email protected]:
[email protected], glob@^8.0.3:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
Expand Down Expand Up @@ -2353,6 +2370,13 @@ is-potential-custom-element-name@^1.0.1:
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==

[email protected]:
version "1.2.1"
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
dependencies:
"@types/estree" "*"

is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
Expand Down Expand Up @@ -2464,6 +2488,11 @@ jackspeak@^2.3.5:
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"

"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==

[email protected], js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
Expand Down Expand Up @@ -2602,6 +2631,13 @@ [email protected]:
chalk "^4.1.0"
is-unicode-supported "^0.1.0"

loose-envify@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"

loupe@^2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
Expand All @@ -2621,6 +2657,13 @@ lru-cache@^6.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==

magic-string@^0.30.3:
version "0.30.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.9.tgz#8927ae21bfdd856310e07a1bc8dd5e73cb6c251d"
integrity sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"

make-dir@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
Expand Down Expand Up @@ -3036,6 +3079,21 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==

react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies:
loose-envify "^1.1.0"
scheduler "^0.23.0"

react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies:
loose-envify "^1.1.0"

readable-stream@~2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
Expand Down Expand Up @@ -3221,6 +3279,13 @@ saxes@^6.0.0:
dependencies:
xmlchars "^2.2.0"

scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies:
loose-envify "^1.1.0"

section-matter@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167"
Expand Down

0 comments on commit e9cbadc

Please sign in to comment.