Skip to content

fix(playground): align package targets and alias self-references with CommonJS #977

Description

@samchon

Problem

The Playground Execute sandbox now supports the three root package.json#exports forms reported in #670, but several adjacent package-target decisions still disagree with Node's CommonJS package algorithm:

  • an array falls through when its first target is valid but the file is missing;
  • an inactive nested condition stops an outer condition map instead of letting it continue;
  • bare or escaping export targets are treated as pack keys;
  • a manifest that mixes subpath and condition keys is accepted;
  • a package installed under an npm alias cannot require itself by its manifest name.

Each difference changes which browser code executes or turns a valid installed dependency into an unavailable-package error.

Evidence

Exact Node 22 controls and the current public sandbox produced:

Case Node Playground sandbox
["./missing.cjs", "./available.cjs"] MODULE_NOT_FOUND for the selected missing target Loads available.cjs
["available.cjs", "./available.cjs"] Skips the invalid target and loads the second Loads the second
{ "require": { "development": "./dev.cjs" }, "default": "./available.cjs" } Loads the outer default Reports unavailable
Export target "available.cjs" ERR_INVALID_PACKAGE_TARGET Loads the file
Mixed { ".": "./root.cjs", "require": "./other.cjs" } ERR_INVALID_PACKAGE_CONFIG Loads a branch
Package mounted as alias, manifest name actual, internal require("actual/sub") Resolves the same package Searches for another mounted package and fails

The alias path is live. installPlaygroundDependencies mounts an npm alias under the requested dependency name while retaining the downloaded manifest's actual name.

Node's package contract also requires export targets to begin with ./ and rejects traversal, node_modules segments, and encoded separator escapes after that prefix. The current resolver removes an optional ./ and concatenates the result with the pack package key.

Consequence surface

The shared reducer owns root, exact subpath, wildcard subpath, nested condition, array, and null targets. The target validator must therefore cover every one of those shapes.

Behavior to preserve:

This issue does not add ESM evaluation, Node builtins, package imports, filesystem access, or every Node resolution error code.

Approach

Replace the flattened string-candidate reducer with a package-target result that distinguishes:

  • resolved target;
  • blocked target;
  • unresolved conditional branch;
  • invalid target.

Validate a string target before pack lookup. Arrays may skip invalid or unresolved entries, but once a valid target is selected, a missing file is a missing module rather than a reason to try another target. Conditional objects continue in manifest order when an active nested branch is unresolved.

Reject manifests that mix subpath and condition keys. For a bare request from inside a mounted package, compare the requested package name with that mount's parsed manifest name; matching requests resolve through the current mount so npm aliases retain self-reference.

Acceptance and verification

pnpm --filter @ttsc/playground build
pnpm --filter @ttsc/test-playground start -- --include=create_sandbox_require
pnpm --filter @ttsc/test-playground start

Coordination

#670 is resolved in public releases beginning with 0.19.0. #820 is the direct manifest-boundary predecessor. This issue covers verified cases outside those issues' completed acceptance matrices and should not reopen either one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions