From d0c924a49ade96361038fa0a2a231c7007cbdea5 Mon Sep 17 00:00:00 2001 From: Titouan Mathis Date: Mon, 10 Aug 2026 10:29:34 +0200 Subject: [PATCH 1/2] Write virtual loaders at resolved package root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The virtual HTML/style/script loader modules were registered with keys relative to the webpack compiler context, landing under the consumer app's node_modules. But config.ts imports them through the bare `@studiometa/playground/-loader.js` specifiers, which webpack resolves via the package's `"./*": "./*"` exports entry to the package's real on-disk root — a different path. This only worked while enhanced-resolve <= 5.20 fell back to legacy resolution for a matched-but-missing exports target. enhanced-resolve >= 5.21 (webpack/enhanced-resolve#399) makes that a hard error, so consumer builds on webpack 5.109+ failed with "Package path ./html-loader.js is exported from package ... but no valid target file was found". Write the virtual files at the package's resolved root so the exports-field target exists on disk. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01HKLcgvaDcjK9ohA3fg7Ssq --- CHANGELOG.md | 6 ++++++ .../src/lib/plugins/PlaygroundLoadersPlugin.ts | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0949145..0b8a88a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Write the virtual HTML/style/script loader modules at the package's resolved on-disk root instead of a path relative to the webpack compiler context, so the bare `@studiometa/playground/-loader.js` imports resolve. These specifiers go through the package's `"./*": "./*"` exports entry, and since enhanced-resolve >= 5.21 (webpack/enhanced-resolve#399) a matched exports target that is missing on disk is a hard error instead of falling back to legacy resolution — so consumer builds on webpack 5.109+ failed with `Package path ./html-loader.js is exported from package ... but no valid target file was found` + ## v0.3.13 - 2026.08.06 ### Fixed diff --git a/packages/playground/src/lib/plugins/PlaygroundLoadersPlugin.ts b/packages/playground/src/lib/plugins/PlaygroundLoadersPlugin.ts index c338a87..a903176 100644 --- a/packages/playground/src/lib/plugins/PlaygroundLoadersPlugin.ts +++ b/packages/playground/src/lib/plugins/PlaygroundLoadersPlugin.ts @@ -1,4 +1,6 @@ -import { existsSync, readFileSync } from 'node:fs'; +import { existsSync, readFileSync, realpathSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { dirname } from 'node:path'; import type { Compiler } from 'webpack'; import VirtualModulesPlugin from 'webpack-virtual-modules'; @@ -25,13 +27,23 @@ export class PlaygroundLoadersPlugin { const virtualModulesConfig = {}; const defaultLoader = 'export default async function loader(value) { return value };'; + // The loaders are imported through the bare `@studiometa/playground/-loader.js` + // specifiers, which webpack resolves via the package's `"./*": "./*"` exports entry to + // this package's real on-disk root. Since enhanced-resolve >= 5.21 (webpack/enhanced-resolve#399), + // a matched exports target that does not exist on disk is a hard error instead of falling back + // to legacy resolution. So the virtual files MUST be written at the package's resolved root; a + // path relative to the compiler context lands under the consumer app and no longer resolves. + const require = createRequire(import.meta.url); + const packageRoot = realpathSync( + dirname(require.resolve('@studiometa/playground/package.json')), + ); + for (const loaderName of this.loaderNames) { const loaderContent = this.loaders && this.loaders[loaderName] && existsSync(this.loaders[loaderName]) ? readFileSync(this.loaders[loaderName]) : defaultLoader; - virtualModulesConfig[`node_modules/@studiometa/playground/${loaderName}-loader.js`] = - loaderContent; + virtualModulesConfig[`${packageRoot}/${loaderName}-loader.js`] = loaderContent; } const virtualModules = new VirtualModulesPlugin(virtualModulesConfig); From 7bf8fbd4aee24cfc65fadf54a86b5be903e0b56b Mon Sep 17 00:00:00 2001 From: Titouan Mathis Date: Mon, 10 Aug 2026 10:30:07 +0200 Subject: [PATCH 2/2] Add PR reference to changelog entry Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01HKLcgvaDcjK9ohA3fg7Ssq --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8a88a..5d65119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed -- Write the virtual HTML/style/script loader modules at the package's resolved on-disk root instead of a path relative to the webpack compiler context, so the bare `@studiometa/playground/-loader.js` imports resolve. These specifiers go through the package's `"./*": "./*"` exports entry, and since enhanced-resolve >= 5.21 (webpack/enhanced-resolve#399) a matched exports target that is missing on disk is a hard error instead of falling back to legacy resolution — so consumer builds on webpack 5.109+ failed with `Package path ./html-loader.js is exported from package ... but no valid target file was found` +- Write the virtual HTML/style/script loader modules at the package's resolved on-disk root instead of a path relative to the webpack compiler context, so the bare `@studiometa/playground/-loader.js` imports resolve. These specifiers go through the package's `"./*": "./*"` exports entry, and since enhanced-resolve >= 5.21 (webpack/enhanced-resolve#399) a matched exports target that is missing on disk is a hard error instead of falling back to legacy resolution — so consumer builds on webpack 5.109+ failed with `Package path ./html-loader.js is exported from package ... but no valid target file was found` ([#77](https://github.com/studiometa/playground/pull/77), [d0c924a](https://github.com/studiometa/playground/commit/d0c924a)) ## v0.3.13 - 2026.08.06