diff --git a/.changeset/yellow-flowers-trade.md b/.changeset/yellow-flowers-trade.md new file mode 100644 index 00000000000..d77fd00be3c --- /dev/null +++ b/.changeset/yellow-flowers-trade.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +update serverBareModulesPlugin warning to use full import path - say dependency isn't available in node_modules instead of package.json diff --git a/integration/compiler-test.ts b/integration/compiler-test.ts index 2242afa0f32..b33e5ddc6bd 100644 --- a/integration/compiler-test.ts +++ b/integration/compiler-test.ts @@ -1,12 +1,14 @@ import path from "path"; import fs from "fs/promises"; import { test, expect } from "@playwright/test"; +import { PassThrough } from "stream"; import { createFixture, createAppFixture, js, json, + createFixtureProject, } from "./helpers/create-fixture"; import type { Fixture, AppFixture } from "./helpers/create-fixture"; import { PlaywrightFixture } from "./helpers/playwright-fixture"; @@ -134,6 +136,14 @@ test.describe("compiler", () => { module: "./esm/index.js", sideEffects: false, }), + "node_modules/@org/package/sub-package/index.js": js` + module.exports.submodule = require("./submodule.js"); + `, + "node_modules/@org/package/sub-package/submodule.js": js` + module.exports = function submodule() { + return "package-with-submodule"; + } + `, "node_modules/@org/package/sub-package/esm/package.json": json({ type: "module", sideEffects: false, @@ -146,14 +156,6 @@ test.describe("compiler", () => { return "package-with-submodule"; } `, - "node_modules/@org/package/sub-package/index.js": js` - module.exports.submodule = require("./submodule.js"); - `, - "node_modules/@org/package/sub-package/submodule.js": js` - module.exports = function submodule() { - return "package-with-submodule"; - } - `, }, }); @@ -320,4 +322,56 @@ test.describe("compiler", () => { expect(magicRemix).toContain(name); } }); + + test.describe("serverBareModulesPlugin", () => { + test("warns when a module isn't installed", async () => { + let buildOutput: string; + let buildStdio = new PassThrough(); + + await expect(() => + createFixtureProject({ + buildStdio, + files: { + "app/routes/index.jsx": js` + import { json } from "@remix-run/node"; + import { useLoaderData } from "@remix-run/react"; + import notInstalledMain from "some-not-installed-module"; + import { notInstalledSub } from "some-not-installed-module/sub"; + + export function loader() { + return json({ main: notInstalledMain(), sub: notInstalledSub() }); + } + + export default function Index() { + let data = useLoaderData(); + return null; + } + `, + }, + }) + ).rejects.toThrowError("Build failed, check the output above"); + + let chunks: Buffer[] = []; + buildOutput = await new Promise((resolve, reject) => { + buildStdio.on("error", (error) => { + reject(error); + }); + buildStdio.on("data", (chunk) => { + chunks.push(Buffer.from(chunk)); + }); + buildStdio.on("end", () => { + resolve(Buffer.concat(chunks).toString("utf8")); + }); + }); + + let importer = path.join("app", "routes", "index.jsx"); + + expect(buildOutput).toContain( + `The path "some-not-installed-module" is imported in ${importer} but "some-not-installed-module" was not found in your node_modules. Did you forget to install it?` + ); + expect(buildOutput).toContain( + `The path "some-not-installed-module/sub" is imported in ${importer} but "some-not-installed-module/sub" was not found in your node_modules. Did you forget to install it?` + ); + }); + }); }); diff --git a/integration/helpers/create-fixture.ts b/integration/helpers/create-fixture.ts index a3ca94c5ead..db61a7eb051 100644 --- a/integration/helpers/create-fixture.ts +++ b/integration/helpers/create-fixture.ts @@ -147,7 +147,6 @@ export async function createFixtureProject(init: FixtureInit): Promise { { overwrite: true } ); if (init.setup) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars let setupSpawn = spawnSync( "node", ["node_modules/@remix-run/dev/dist/cli.js", "setup", init.setup], @@ -178,9 +177,7 @@ function build(projectDir: string, buildStdio?: Writable, sourcemap?: boolean) { if (sourcemap) { buildArgs.push("--sourcemap"); } - let buildSpawn = spawnSync("node", buildArgs, { - cwd: projectDir, - }); + let buildSpawn = spawnSync("node", buildArgs, { cwd: projectDir }); // These logs are helpful for debugging. Remove comments if needed. // console.log("spawning @remix-run/dev/cli.js `build`:\n"); @@ -188,16 +185,17 @@ function build(projectDir: string, buildStdio?: Writable, sourcemap?: boolean) { // console.log(" " + buildSpawn.stdout.toString("utf-8")); // console.log(" STDERR:"); // console.log(" " + buildSpawn.stderr.toString("utf-8")); - if (buildSpawn.error || buildSpawn.status) { - console.error(buildSpawn.stderr.toString("utf-8")); - throw buildSpawn.error || new Error(`Build failed, check the output above`); - } if (buildStdio) { buildStdio.write(buildSpawn.stdout.toString("utf-8")); buildStdio.write(buildSpawn.stderr.toString("utf-8")); buildStdio.end(); } + + if (buildSpawn.error || buildSpawn.status) { + console.error(buildSpawn.stderr.toString("utf-8")); + throw buildSpawn.error || new Error(`Build failed, check the output above`); + } } async function writeTestFiles(init: FixtureInit, dir: string) { diff --git a/packages/remix-dev/compiler/plugins/serverBareModulesPlugin.ts b/packages/remix-dev/compiler/plugins/serverBareModulesPlugin.ts index 59ca2662dc9..0ace01c072f 100644 --- a/packages/remix-dev/compiler/plugins/serverBareModulesPlugin.ts +++ b/packages/remix-dev/compiler/plugins/serverBareModulesPlugin.ts @@ -71,14 +71,14 @@ export function serverBareModulesPlugin( !/\bnode_modules\b/.test(importer) ) { try { - require.resolve(packageName); + require.resolve(path); } catch (error) { onWarning( `The path "${path}" is imported in ` + `${relative(process.cwd(), importer)} but ` + - `${packageName} is not listed in your package.json dependencies. ` + + `"${path}" was not found in your node_modules. ` + `Did you forget to install it?`, - packageName + path ); } }