diff --git a/CHANGELOG.md b/CHANGELOG.md index 0949145..5d65119 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` ([#77](https://github.com/studiometa/playground/pull/77), [d0c924a](https://github.com/studiometa/playground/commit/d0c924a)) + ## 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);