Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat]: CLI support for package.json subpath imports #2960

Open
2 tasks done
jeremy-code opened this issue Mar 9, 2024 · 8 comments
Open
2 tasks done

[feat]: CLI support for package.json subpath imports #2960

jeremy-code opened this issue Mar 9, 2024 · 8 comments

Comments

@jeremy-code
Copy link

Feature description

In TypeScript 5.4, subpath imports are now supported. In components.json, if the subpath import is added as an alias like:

"aliases": {
    "components": "#components"
}

It will create a new folder called #components, and put all the components in there. Conversely, if the alias is set to the actual components directory (e.g. ./src/components/ui), an error will be thrown as an array of errors like:

{
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": [
      "resolvedPaths",
      "components"
    ],
    "message": "Required"
},

Not setting any alias at all results in error "Invalid configuration found in ../components.json".

In the meantime, I have set it with the subpath imports as the alias, and added bash commands to move it to the correctly place, but this isn't ideal.

"ui:add": "yarn dlx shadcn-ui@latest add && mv ./#components/ui/* ./src/components/ui/ && rm -rf ./#components",

Affected component/components

No response

Additional Context

No response

Before submitting

  • I've made research efforts and searched the documentation
  • I've searched for existing issues and PRs
@shadcn
Copy link
Collaborator

shadcn commented Jun 10, 2024

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.

@shadcn shadcn closed this as completed Jun 10, 2024
@jam-fran
Copy link

Turborepo now officially recommends node subpath imports for internal packages in their docs:

A library that is being transpiled by its consumer cannot use the compilerOptions.paths configuration because TypeScript assumes that source code is being transpiled in the package where it is written. If you're using Typescript 5.4 or later, we recommend using Node.js subpath imports.

Thanks @jeremy-code for the workaround, but would be amazing if shadcn supported this at some point, since I imagine this will become much more prevalent in the near future.

@sqmasep
Copy link

sqmasep commented Oct 3, 2024

I'm facing a similar issue, it would be cool that the CLI get fixed on the path alias. Currently, it seems like if the CLI detects that in the tsconfig there is a "wrong" alias (such as #utils), it fallbacks to #/lib/utils.

Node subpath can only begin with a # so it doesn't get confused with public scoped packages (that start with @)
The problem is that node subpath doesn't allow an alias that starts with #/. And shadcn CLI doesn't seem to work with anything that don't start with <character>/. This trailing slash makes the problem, basically

The only way to fix it is whenever you add a new component with the CLI, you need to fix the path alias by hand (unless someone else finds another solution?)

@SanderCokart
Copy link

Reactivate this please!

@shadcn shadcn reopened this Nov 27, 2024
@jeremy-code
Copy link
Author

jeremy-code commented Dec 2, 2024

A workaround that involves not using aliases is using Node.js self-referencing.

For instance, see the following code:

// package.json
{
  "name": "@repo/ui",
  "exports": {
    "./globals.css": "./globals.css",
    "./postcss.config": "./postcss.config.js",
    "./tailwind.config": "./tailwind.config.ts",
    "./utils": "./utils.ts",
    "./components/*": "./components/*.tsx",
    "./components/ui/*": "./components/ui/*.tsx"
  },
}

// components.json
{
  "aliases": {
    "utils": "@repo/ui/utils",
    "components": "@repo/ui/components"
  }
}

Then, internally (in @repo/ui), imports will look like import { cn } from "@repo/ui/utils"; and in external apps like import { Switch } from "@repo/ui/components/ui/switch";. If you're not using a module bundler, you'll have to update the export map for .js.

IMO, due to the package structure (few imports), this is more useful than using aliasing since it just bases everything based on what you export. The only issue is if you have some internal tool in the library that you don't want to export, but at least for shadcn/ui CLI, there isn't any instances of this.


EDIT:

shadcn/ui has released experimental monorepo support using the internal packages (@workspace/ui) structure.

@bdbergeron
Copy link

After playing around with things for a while, I was able to get subpath imports working with shadcn/ui components nearly perfectly. My package is a Compiled Package, thus the dist output path being referenced, but I believe this should work if yours is a Just-In-Time Package if you update the imports path to point to your TS files instead of the compiled files.

// package.json
{
  ...
  "imports": {
    "#*": "./dist/*.js"
  },
  ...
}
// tsconfig.json
{
  ...
  "compilerOptions": {
    "paths": {
      "#*": ["./src/*"]
    },
  }
  ...
}
// components.json
{
  ...
  "aliases": {
    "components": "#components",
    "hooks": "#hooks",
    "lib": "#lib",
    "ui": "#components/ui",
    "utils": "#lib/utils"
  }
  ...
}

It is unfortunate to need the TS path alias in addition to the subpath import, but this gets you 99% there. The only thing that does not seem to work with this setup is the generated cn import, which for some reason gets imported as #/lib/utils instead of #lib/utils. As @sqmasep pointed out above, you'll have to fix this import by hand.

@jdfm
Copy link

jdfm commented Jan 2, 2025

A workaround that involves not using aliases is using Node.js self-referencing.

For instance, see the following code:

// package.json
{
  "name": "@repo/ui",
  "exports": {
    "./globals.css": "./globals.css",
    "./postcss.config": "./postcss.config.js",
    "./tailwind.config": "./tailwind.config.ts",
    "./utils": "./utils.ts",
    "./components/*": "./components/*.tsx",
    "./components/ui/*": "./components/ui/*.tsx"
  },
}

// components.json
{
  "aliases": {
    "utils": "@repo/ui/utils",
    "components": "@repo/ui/components"
  }
}

Then, internally (in @repo/ui), imports will look like import { cn } from "@repo/ui/utils"; and in external apps like import { Switch } from "@repo/ui/components/ui/switch";. If you're not using a module bundler, you'll have to update the export map for .js.

IMO, due to the package structure (few imports), this is more useful than using aliasing since it just bases everything based on what you export. The only issue is if you have some internal tool in the library that you don't want to export, but at least for shadcn/ui CLI, there isn't any instances of this.

I ended up going the self-referencing route, here's what the exports (in package.json) ended up looking like:

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

And in tsconfig.json:

    "paths": {
      "@repo/ui/*": ["./src/*"]
    }

This helped me get rid of .ts/tsx extensions at the app level when importing code, allows bundlers and typescript to properly identify what code is relevant, and allows shadcn cli to work (for the most part, the bugs that exist around the utils import at the moment still affect us here). This also helped me keep this library configured as an "internal" typescript package as outlined in https://turbo.build/blog/you-might-not-need-typescript-project-references#internal-typescript-packages

I think I ran into issues with transpilePackages config in nextjs when I tried to use the "imports" setup in package.json and tried to build, not entirely sure as it's been a while since I tested that particular setup.

@shadcn
Copy link
Collaborator

shadcn commented Mar 2, 2025

This issue has been automatically marked as stale due to two years of inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you.

@shadcn shadcn added stale? Is this issue stale? and removed stale? Is this issue stale? Stale labels Mar 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants