Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<name>-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
Expand Down
18 changes: 15 additions & 3 deletions packages/playground/src/lib/plugins/PlaygroundLoadersPlugin.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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/<name>-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);
Expand Down
Loading