Skip to content

@simple-module-py/ui exports map: './*': './src/*' can't resolve nested subpaths or extension-less imports #115

Description

@antosubash

Summary

@simple-module-py/ui@0.0.3's exports map can't resolve the imports its own consumers (and the built-in modules) use. The map is:

"exports": {
".": { "types": "./src/index.ts", "default": "./src/index.ts" },
"./*": "./src/*"
}

Two problems compose into a hard fail:

  1. Extension stripping isn't applied to exports patterns. Per the Node spec, "./*": "./src/*" resolves ./components/PageShell to ./src/components/PageShellliterally. There's no file at that exact path — the file is ./src/components/PageShell.tsx. Node/Vite won't try .tsx/.ts fallbacks once an exports field is present (unlike legacy main/module resolution).
  2. The single wildcard collapses every depth into one rule. Even if the extension worked, the map can't distinguish ./components/* (→ .tsx) from ./hooks/* (→ .ts) from ./styles/* (→ .css). They have to be separate entries.

Reproduction

Fresh sm new 0.0.7 host with #114's deps worked around (or just trust the imports below — they're directly from the users module wheel and the @simple-module-py/ui source):

Error: The following dependencies are imported but could not be resolved:
@simple-module-py/ui/components/PageShell (-> src/components/PageShell.tsx)
@simple-module-py/ui/components/NavIcon (-> src/components/NavIcon.tsx)
@simple-module-py/ui/components/ui/button (-> src/components/ui/button.tsx)
@simple-module-py/ui/components/ui/card (-> src/components/ui/card.tsx)
@simple-module-py/ui/components/ui/input (-> src/components/ui/input.tsx)
@simple-module-py/ui/components/ui/label (-> src/components/ui/label.tsx)
@simple-module-py/ui/components/ui/badge (-> src/components/ui/badge.tsx)
@simple-module-py/ui/components/ui/checkbox (-> src/components/ui/checkbox.tsx)
@simple-module-py/ui/components/ui/alert-dialog (-> src/components/ui/alert-dialog.tsx)
@simple-module-py/ui/components/ui/table (-> src/components/ui/table.tsx)
@simple-module-py/ui/components/ui/tabs (-> src/components/ui/tabs.tsx)
@simple-module-py/ui/components/ui/select (-> src/components/ui/select.tsx)
@simple-module-py/ui/layouts/AuthenticatedLayout (-> src/layouts/AuthenticatedLayout.tsx)
@simple-module-py/ui/layouts/AuthCardShell (-> src/layouts/AuthCardShell.tsx)

Source-tree layout for context (extracted from client_app/node_modules/@simple-module-py/ui/src/):

src/
index.ts
types.ts
components/
ErrorBoundary.tsx ErrorScreen.tsx LocaleSwitcher.tsx NavIcon.tsx
PageShell.tsx
ui/
accordion.tsx alert-dialog.tsx alert.tsx ... (50+ shadcn components)
layouts/
AdminLayout.tsx AppLayout.tsx AuthCardShell.tsx
AuthenticatedLayout.tsx PublicLayout.tsx SidebarLayout.tsx
hooks/
*.ts
lib/
*.ts
styles/
*.css

Proposed fix

Replace the exports field in packages/ui/package.json with explicit per-directory wildcards that pin the file extension:

"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./components/*": {
"types": "./src/components/*.tsx",
"default": "./src/components/*.tsx"
},
"./components/ui/*": {
"types": "./src/components/ui/*.tsx",
"default": "./src/components/ui/*.tsx"
},
"./layouts/*": {
"types": "./src/layouts/*.tsx",
"default": "./src/layouts/*.tsx"
},
"./hooks/*": {
"types": "./src/hooks/*.ts",
"default": "./src/hooks/*.ts"
},
"./lib/*": {
"types": "./src/lib/*.ts",
"default": "./src/lib/*.ts"
},
"./types": "./src/types.ts",
"./styles/*": "./src/styles/*"
}

Notes:

  • ./components/ui/* is listed after./components/*. Node picks the most-specific pattern, but listing the deeper one explicitly avoids any ambiguity in older resolvers.
  • This deliberately drops the "./*": "./src/*" catch-all. With it back in, ambiguous resolutions can pick the wrong target. Anyone wanting raw access to a non-listed file should add an explicit entry.
  • All entries point at .tsx/.ts source. Consumers compile via vite/tsc — there's no separate dist/ build step, which matches what the package already ships ("files": ["src", "README.md"]).

Sanity check

After the fix, import Button from '@simple-module-py/ui/components/ui/button' resolves to client_app/node_modules/@simple-module-py/ui/src/components/ui/button.tsx and Vite's React plugin compiles it as JSX.

Related

Workaround

Patch client_app/node_modules/@simple-module-py/ui/package.json after npm install (or use a postinstall script). Won't survive lockfile regeneration cleanly.

Environment

  • @simple-module-py/ui 0.0.3
  • vite 6.4.2 (from current scaffold's npm install)
  • Node 20+

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingjavascriptPull requests that update javascript code

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions