Search code examples
javascriptreactjstypescriptwebpackvite

Adding Submodule Paths in a Vite React Library


I have a Vite-based React library, currently structured like this:

import { Button, Typography, Box, Flex, Color, TypographyVariant } from '@placeholder-library';

I want to separate the imports to add submodules so that components and shared are imported from different paths:

import { Button, Typography, Box, Flex } from '@placeholder-library/components’;
import { Color, TypographyVariant } from ‘@placeholder-library/shared’;

index.ts

import './index.scss';
export * from './components';
export * from './shared';

vite.config.ts:

import react from '@vitejs/plugin-react';
import path from 'path';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import svgr from 'vite-plugin-svgr';
import tsconfigPaths from 'vite-tsconfig-paths';
import commonjs from 'vite-plugin-commonjs';

export default defineConfig({
  resolve: {
    alias: {
      src: path.resolve(__dirname, './src'),
    },
  },
  build: {
    outDir: 'build',
    lib: {
      entry: './src/index.ts',
      name: 'Placeholder Library',
      fileName: 'index',
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: [
        {
          globals: {
            react: 'React',
            'react-dom': 'ReactDOM',
          },
        },
        {
          dir: 'build/cjs',
          format: 'cjs',
          globals: {
            react: 'React',
            'react-dom': 'ReactDOM',
          },
        },
        {
          dir: 'build/esm',
          format: 'esm',
          globals: {
            react: 'React',
            'react-dom': 'ReactDOM',
          },
        },
      ],
    },
    sourcemap: true,
    emptyOutDir: true,
  },
  plugins: [
    svgr(),
    react(),
    commonjs(),
    tsconfigPaths(),
    dts({
      outDir: ['build/cjs', 'build/esm', 'build'],
      include: ['./src/**/*'],
      exclude: ['**/*.stories.*'],
    }),
  ],
});

package.json:

{
  "name": "@placeholder-library",
  "version": "0.0.26",
  "description": "Placeholder Library components library",
  "license": "ISC",
  "main": "build/cjs/index.js",
  "module": "build/index.mjs",
  "files": ["*"],
  "scripts": {
    "build": "tsc && vite build",
    "build-storybook": "storybook build",
    "build-storybook-docs": "storybook build --docs",
    "dev": "vite",
    "format": "prettier --write .",
    "lint:fix": "eslint . --fix --ignore-path .gitignore",
    "prepare": "husky install",
    "preview": "vite preview",
    "storybook": "storybook dev -p 6006",
    "storybook-docs": "storybook dev --docs"
  },
  "dependencies": {
    "..."
  },
  "devDependencies": {
    "..."
  },
  "peerDependencies": {
    "react": "^18.2.0"
  }
}

my folder structure: My folder structure

src
    index.ts
    components
        index.ts
        Button
            index.ts
    shared
        hooks
        index.ts

index.ts in components:

export * from './Button'

How can I configure Vite and my package structure to achieve this separation of components and shared/utils? Any advice or examples would be greatly appreciated.

I attempted to modify the vite.config.ts file to include separate entries for components and shared/utils, but I couldn't figure out how to properly configure the paths. I also tried to adjust the package.json file to specify different entry points for components and shared/utils, but I wasn't sure how to structure it correctly.

import { Button, Typography, Box, Flex } from '@placeholder-library/components’;

import { Color, TypographyVariant } from ‘@placeholder-library/shared’;

However, I couldn't find a clear example or documentation on how to set this up in a Vite-based React library. Any guidance or examples on how to achieve this would be greatly appreciated.


Solution

  • I think a demo would be easier than going back and forth: https://github.com/quyentho/submodule-demo

    You can check my dist/ folder in the placeholder-lib to see if your generated build has similar structure. I have no problem to import like this in my consumer:

    import { Button } from "placeholder-lib/components";
    import useMyHook from "placeholder-lib/shared";
    

    I guess, your problem could be missing these export lines in package.json

      "exports": {
        ".": "./dist/index.js",
        "./components": "./dist/components/index.js",
        "./shared": "./dist/shared/index.js"
      },