@eventuras/vite-config 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,12 @@ Shared Vite configurations for Eventuras monorepo libraries.
6
6
 
7
7
  This package provides reusable Vite configuration presets for different types of libraries in the Eventuras monorepo. It helps maintain consistency, reduces duplication, and makes it easier to update build configurations across all libraries.
8
8
 
9
+ ## Requirements
10
+
11
+ - Node.js 24+
12
+ - Vite 7 or 8 (peer dependency)
13
+ - TypeScript 6 in the consuming package — the declaration step (`vite-plugin-dts` v5) needs the TypeScript JS Compiler API, which TypeScript 7 no longer ships by default
14
+
9
15
  ## Presets
10
16
 
11
17
  ### Vanilla Library (`vanilla-lib`)
@@ -169,3 +175,14 @@ export default defineReactLibConfig({
169
175
  ### Build errors with Next.js
170
176
  - Use `next-lib` preset instead of `react-lib`
171
177
  - Check that Next.js packages are in `external` array
178
+
179
+ ### `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`
180
+ - Upgrade to 0.3.0 or later. Versions up to 0.2.2 exported raw TypeScript sources, which Node refuses to load from `node_modules`. Workarounds such as `NODE_OPTIONS="--import tsx"` or `vite build --configLoader runner` are no longer needed and can be removed.
181
+
182
+ ## Development
183
+
184
+ The published package is compiled output, not sources: `pnpm build` runs `tsc` and
185
+ emits `dist/` (ESM + declarations), and `exports` points there. Do not repoint
186
+ `exports` at `src/` — Node never strips types for files under `node_modules`, so
187
+ that breaks every consumer installing from the registry. `pnpm verify:packaging`
188
+ at the repo root checks this.
package/dist/base.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type UserConfig } from 'vite';
2
+ /**
3
+ * Base Vite configuration with common defaults for all Eventuras packages.
4
+ */
5
+ export declare function defineBaseConfig(config?: UserConfig): UserConfig;
package/dist/base.js ADDED
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite';
2
+ /**
3
+ * Base Vite configuration with common defaults for all Eventuras packages.
4
+ */
5
+ export function defineBaseConfig(config = {}) {
6
+ return defineConfig({
7
+ ...config,
8
+ build: {
9
+ ...config.build,
10
+ // Common build optimizations
11
+ minify: false, // Libraries should not be minified
12
+ sourcemap: true, // Always generate sourcemaps for debugging
13
+ },
14
+ });
15
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Reads the consumer package's package.json and returns RegExp patterns that
3
+ * mark every runtime dependency (and its subpaths) as external.
4
+ *
5
+ * Bundling runtime deps into a library is harmful: it duplicates code, breaks
6
+ * `instanceof` checks across module boundaries (the consumer's class identity
7
+ * differs from the bundled copy), and inflates installed size. Consumers
8
+ * already install these deps via the lib's own package.json.
9
+ */
10
+ export declare function getRuntimeDependencyExternals(cwd?: string): RegExp[];
11
+ /**
12
+ * Externalize Node built-ins in both forms — `node:fs` (always matched by the
13
+ * regex) and the bare `fs` form (matched against the explicit list, since the
14
+ * names overlap with userland packages and can't be regex-distinguished).
15
+ */
16
+ export declare const NODE_BUILTINS_EXTERNAL: (string | RegExp)[];
@@ -1,13 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { builtinModules } from 'node:module';
3
3
  import { resolve } from 'node:path';
4
-
5
- interface PackageJson {
6
- dependencies?: Record<string, string>;
7
- peerDependencies?: Record<string, string>;
8
- optionalDependencies?: Record<string, string>;
9
- }
10
-
11
4
  /**
12
5
  * Reads the consumer package's package.json and returns RegExp patterns that
13
6
  * mark every runtime dependency (and its subpaths) as external.
@@ -17,29 +10,25 @@ interface PackageJson {
17
10
  * differs from the bundled copy), and inflates installed size. Consumers
18
11
  * already install these deps via the lib's own package.json.
19
12
  */
20
- export function getRuntimeDependencyExternals(cwd: string = process.cwd()): RegExp[] {
21
- const pkgPath = resolve(cwd, 'package.json');
22
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;
23
-
24
- const names = [
25
- ...Object.keys(pkg.dependencies ?? {}),
26
- ...Object.keys(pkg.peerDependencies ?? {}),
27
- ...Object.keys(pkg.optionalDependencies ?? {}),
28
- ];
29
-
30
- return names.map(name => new RegExp(`^${escapeRegex(name)}(/.*)?$`));
13
+ export function getRuntimeDependencyExternals(cwd = process.cwd()) {
14
+ const pkgPath = resolve(cwd, 'package.json');
15
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
16
+ const names = [
17
+ ...Object.keys(pkg.dependencies ?? {}),
18
+ ...Object.keys(pkg.peerDependencies ?? {}),
19
+ ...Object.keys(pkg.optionalDependencies ?? {}),
20
+ ];
21
+ return names.map(name => new RegExp(`^${escapeRegex(name)}(/.*)?$`));
31
22
  }
32
-
33
23
  /**
34
24
  * Externalize Node built-ins in both forms — `node:fs` (always matched by the
35
25
  * regex) and the bare `fs` form (matched against the explicit list, since the
36
26
  * names overlap with userland packages and can't be regex-distinguished).
37
27
  */
38
- export const NODE_BUILTINS_EXTERNAL: (string | RegExp)[] = [
39
- /^node:/,
40
- ...builtinModules,
28
+ export const NODE_BUILTINS_EXTERNAL = [
29
+ /^node:/,
30
+ ...builtinModules,
41
31
  ];
42
-
43
- function escapeRegex(s: string): string {
44
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32
+ function escapeRegex(s) {
33
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
45
34
  }
@@ -0,0 +1,14 @@
1
+ import type { UserConfig } from 'vite';
2
+ import { type ReactLibConfig } from './react-lib.js';
3
+ export interface NextLibConfig extends Omit<ReactLibConfig, 'external'> {
4
+ /**
5
+ * Additional external dependencies beyond the Next.js defaults and the
6
+ * package.json auto-externalization (via the underlying React preset).
7
+ */
8
+ external?: (string | RegExp)[];
9
+ }
10
+ /**
11
+ * Vite configuration preset for Next.js-compatible React libraries.
12
+ * Extends the React library config with Next.js-specific externals and defaults.
13
+ */
14
+ export declare function defineNextLibConfig(config: NextLibConfig): UserConfig;
@@ -0,0 +1,24 @@
1
+ import { defineReactLibConfig } from './react-lib.js';
2
+ /**
3
+ * Vite configuration preset for Next.js-compatible React libraries.
4
+ * Extends the React library config with Next.js-specific externals and defaults.
5
+ */
6
+ export function defineNextLibConfig(config) {
7
+ const { external = [], ...reactConfig } = config;
8
+ // Next.js externals - include all next/* paths to avoid bundling internal modules
9
+ const nextExternals = [
10
+ 'next',
11
+ 'next/image',
12
+ 'next/link',
13
+ 'next/navigation',
14
+ 'next/router',
15
+ 'next/headers',
16
+ /^next\//, // Externalize all next/* imports
17
+ ];
18
+ return defineReactLibConfig({
19
+ ...reactConfig,
20
+ external: [...nextExternals, ...external],
21
+ // Next.js libraries should always preserve client directives
22
+ preserveUseClientDirectives: true,
23
+ });
24
+ }
@@ -0,0 +1,67 @@
1
+ import { type UserConfig } from 'vite';
2
+ export interface ReactLibConfig {
3
+ /**
4
+ * Library entry point(s). Can be:
5
+ * - A string path: 'src/index.ts'
6
+ * - An object with multiple entries: { index: 'src/index.ts', utils: 'src/utils.ts' }
7
+ * - A glob pattern: 'src/** /index.ts' (auto-discovers multiple entry points)
8
+ */
9
+ entry: string | Record<string, string>;
10
+ /**
11
+ * Additional external dependencies to exclude from the bundle, on top of the
12
+ * runtime deps read from the consumer's package.json and the React core
13
+ * externals. Pass `false` to opt out of package.json auto-externalization.
14
+ */
15
+ external?: (string | RegExp)[] | false;
16
+ /**
17
+ * Enable Tailwind CSS support via @tailwindcss/vite plugin.
18
+ * @default false
19
+ */
20
+ tailwind?: boolean;
21
+ /**
22
+ * Preserve module structure in output.
23
+ * When true, creates a dist structure mirroring src structure.
24
+ * When false, bundles everything into fewer files.
25
+ * @default true
26
+ */
27
+ preserveModules?: boolean;
28
+ /**
29
+ * Enable 'use client' directive preservation for Next.js React Server Components.
30
+ * @default true
31
+ */
32
+ preserveUseClientDirectives?: boolean;
33
+ /**
34
+ * Use SWC instead of Babel for React transformation (faster builds).
35
+ * @default false
36
+ */
37
+ useSWC?: boolean;
38
+ /**
39
+ * TypeScript declaration options.
40
+ */
41
+ dts?: {
42
+ /**
43
+ * Entry root for DTS generation.
44
+ * @default 'src'
45
+ */
46
+ entryRoot?: string;
47
+ /**
48
+ * Output directory for .d.ts files.
49
+ * @default 'dist'
50
+ */
51
+ outDir?: string;
52
+ /**
53
+ * Roll up types into a single .d.ts file.
54
+ * @default false
55
+ */
56
+ rollupTypes?: boolean;
57
+ };
58
+ /**
59
+ * Additional Vite configuration to merge.
60
+ */
61
+ viteConfig?: UserConfig;
62
+ }
63
+ /**
64
+ * Vite configuration preset for React libraries.
65
+ * Includes React plugin, TypeScript declarations, and common React externals.
66
+ */
67
+ export declare function defineReactLibConfig(config: ReactLibConfig): UserConfig;
@@ -0,0 +1,156 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import tailwindcss from '@tailwindcss/vite';
4
+ import dts from 'vite-plugin-dts';
5
+ import { resolve } from 'node:path';
6
+ import { glob } from 'glob';
7
+ import fs from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import { getRuntimeDependencyExternals, NODE_BUILTINS_EXTERNAL } from './externals.js';
10
+ /**
11
+ * ESM has no `require`. This one is bound to this module so the SWC plugin can
12
+ * stay lazily loaded — it drags in the native `@swc/core` binary, which most
13
+ * consumers never need.
14
+ */
15
+ const requireFromHere = createRequire(import.meta.url);
16
+ /**
17
+ * Strip leading whitespace and any interleaved mix of line and block comments,
18
+ * so a directive check sees the first real token. Sequential per-style passes
19
+ * miss mixed orders (a line comment, then a block comment, then the directive).
20
+ */
21
+ function stripLeadingComments(code) {
22
+ return code.replace(/^(?:\s+|\/\*[\s\S]*?\*\/\s*|\/\/.*(?:\r?\n|$)\s*)+/, '');
23
+ }
24
+ /**
25
+ * Plugin to preserve 'use client' directives in React Server Components.
26
+ * This ensures client-side code is properly marked when building for Next.js.
27
+ */
28
+ function preserveUseClient() {
29
+ return {
30
+ name: 'preserve-use-client',
31
+ enforce: 'post',
32
+ generateBundle(_options, bundle) {
33
+ for (const [_fileName, chunk] of Object.entries(bundle)) {
34
+ if (chunk.type === 'chunk') {
35
+ const chunkData = chunk;
36
+ // Check if any source module had 'use client'
37
+ const hasClientDirective = chunkData.moduleIds?.some((id) => {
38
+ try {
39
+ const content = fs.readFileSync(id, 'utf-8');
40
+ // Remove leading comments to check for 'use client'
41
+ const withoutComments = stripLeadingComments(content);
42
+ return (withoutComments.startsWith("'use client'") ||
43
+ withoutComments.startsWith('"use client"'));
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ });
49
+ if (hasClientDirective) {
50
+ // Ignore leading banner/license comments (e.g. Rollup's
51
+ // output.banner) when checking for the directive, so we don't
52
+ // prepend a duplicate above a directive that's already there.
53
+ const codeStart = stripLeadingComments(chunkData.code);
54
+ if (!codeStart.startsWith("'use client'") && !codeStart.startsWith('"use client"')) {
55
+ chunkData.code = `'use client';\n${chunkData.code}`;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ },
61
+ };
62
+ }
63
+ /**
64
+ * Vite configuration preset for React libraries.
65
+ * Includes React plugin, TypeScript declarations, and common React externals.
66
+ */
67
+ export function defineReactLibConfig(config) {
68
+ const { entry, external, tailwind = false, preserveModules = true, preserveUseClientDirectives = true, useSWC = false, dts: dtsOptions = {}, viteConfig = {}, } = config;
69
+ const autoExternals = external === false ? [] : getRuntimeDependencyExternals();
70
+ const userExternals = Array.isArray(external) ? external : [];
71
+ // Determine entry points
72
+ let entryPoints;
73
+ if (typeof entry === 'string' && entry.includes('*')) {
74
+ entryPoints = glob.sync(resolve(process.cwd(), entry));
75
+ }
76
+ else if (typeof entry === 'string') {
77
+ entryPoints = resolve(process.cwd(), entry);
78
+ }
79
+ else {
80
+ entryPoints = entry;
81
+ }
82
+ // Common React externals
83
+ const reactExternals = ['react', 'react-dom', 'react/jsx-runtime'];
84
+ const plugins = [];
85
+ // Add React plugin (SWC or standard)
86
+ if (useSWC) {
87
+ // Load the SWC plugin only when actually requested
88
+ const reactSwc = requireFromHere('@vitejs/plugin-react-swc').default;
89
+ plugins.push(reactSwc());
90
+ }
91
+ else {
92
+ plugins.push(react());
93
+ }
94
+ // Add Tailwind if enabled
95
+ if (tailwind) {
96
+ plugins.push(tailwindcss());
97
+ }
98
+ // Add DTS plugin
99
+ plugins.push(dts({
100
+ entryRoot: dtsOptions.entryRoot || 'src',
101
+ // vite-plugin-dts v5 renamed `outDir` -> `outDirs` and
102
+ // `rollupTypes` -> `bundleTypes`; the preset keeps the old names.
103
+ outDirs: dtsOptions.outDir || 'dist',
104
+ include: ['src/**/*'],
105
+ exclude: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx', '**/*.stories.tsx'],
106
+ copyDtsFiles: true,
107
+ bundleTypes: dtsOptions.rollupTypes || false,
108
+ }));
109
+ // Add use client preservation if enabled
110
+ if (preserveUseClientDirectives) {
111
+ plugins.push(preserveUseClient());
112
+ }
113
+ // Add custom plugins from viteConfig
114
+ if (viteConfig.plugins) {
115
+ plugins.push(...viteConfig.plugins);
116
+ }
117
+ return defineConfig({
118
+ plugins,
119
+ resolve: {
120
+ alias: {
121
+ '@': resolve(process.cwd(), './src'),
122
+ ...viteConfig.resolve?.alias,
123
+ },
124
+ ...viteConfig.resolve,
125
+ },
126
+ build: {
127
+ minify: false,
128
+ sourcemap: true,
129
+ lib: {
130
+ entry: entryPoints,
131
+ formats: ['es'],
132
+ },
133
+ rollupOptions: {
134
+ ...viteConfig.build?.rollupOptions,
135
+ external: [
136
+ ...reactExternals,
137
+ ...autoExternals,
138
+ ...NODE_BUILTINS_EXTERNAL,
139
+ ...userExternals,
140
+ ],
141
+ output: {
142
+ preserveModules,
143
+ ...(preserveModules && {
144
+ preserveModulesRoot: 'src',
145
+ entryFileNames: '[name].js',
146
+ }),
147
+ ...viteConfig.build?.rollupOptions?.output,
148
+ },
149
+ },
150
+ // Spread other build options from viteConfig except lib and rollupOptions
151
+ ...Object.fromEntries(Object.entries(viteConfig.build || {}).filter(([key]) => key !== 'lib' && key !== 'rollupOptions')),
152
+ },
153
+ // Spread other viteConfig options except build, plugins, and resolve
154
+ ...Object.fromEntries(Object.entries(viteConfig).filter(([key]) => key !== 'build' && key !== 'plugins' && key !== 'resolve')),
155
+ });
156
+ }
@@ -0,0 +1,35 @@
1
+ import { type UserConfig } from 'vite';
2
+ export interface VanillaLibConfig {
3
+ /**
4
+ * Library entry point(s). Can be a string or an object with multiple entries.
5
+ * @example 'src/index.ts'
6
+ * @example { index: 'src/index.ts', utils: 'src/utils.ts' }
7
+ */
8
+ entry: string | Record<string, string>;
9
+ /**
10
+ * Library name for UMD builds (optional).
11
+ */
12
+ name?: string;
13
+ /**
14
+ * Additional external dependencies to exclude from the bundle, on top of the
15
+ * runtime deps read from the consumer's package.json. Pass `false` to opt out
16
+ * of the package.json auto-externalization (rarely what you want).
17
+ */
18
+ external?: (string | RegExp)[] | false;
19
+ /**
20
+ * Additional Vite configuration to merge.
21
+ */
22
+ viteConfig?: UserConfig;
23
+ }
24
+ /**
25
+ * Vite configuration preset for vanilla TypeScript libraries.
26
+ *
27
+ * Defaults that matter for libraries:
28
+ * - All runtime deps (dependencies + peerDependencies + optionalDependencies)
29
+ * are externalized so consumers get one copy from their node_modules instead
30
+ * of inlined+mangled copies inside the published bundle.
31
+ * - Minification is OFF — consumers minify their own bundle, and unminified
32
+ * output preserves class names (so `instanceof` and stack traces work).
33
+ * - Sourcemaps ON for debuggable consumer stack traces.
34
+ */
35
+ export declare function defineVanillaLibConfig(config: VanillaLibConfig): UserConfig;
@@ -0,0 +1,51 @@
1
+ import { defineConfig } from 'vite';
2
+ import dts from 'vite-plugin-dts';
3
+ import { resolve } from 'node:path';
4
+ import { getRuntimeDependencyExternals, NODE_BUILTINS_EXTERNAL } from './externals.js';
5
+ /**
6
+ * Vite configuration preset for vanilla TypeScript libraries.
7
+ *
8
+ * Defaults that matter for libraries:
9
+ * - All runtime deps (dependencies + peerDependencies + optionalDependencies)
10
+ * are externalized so consumers get one copy from their node_modules instead
11
+ * of inlined+mangled copies inside the published bundle.
12
+ * - Minification is OFF — consumers minify their own bundle, and unminified
13
+ * output preserves class names (so `instanceof` and stack traces work).
14
+ * - Sourcemaps ON for debuggable consumer stack traces.
15
+ */
16
+ export function defineVanillaLibConfig(config) {
17
+ const { entry, name, external, viteConfig = {} } = config;
18
+ const autoExternals = external === false ? [] : getRuntimeDependencyExternals();
19
+ const userExternals = Array.isArray(external) ? external : [];
20
+ return defineConfig({
21
+ plugins: [
22
+ dts({
23
+ include: ['src/**/*'],
24
+ exclude: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
25
+ }),
26
+ ...(viteConfig.plugins || []),
27
+ ],
28
+ build: {
29
+ minify: false,
30
+ sourcemap: true,
31
+ lib: {
32
+ entry: typeof entry === 'string' ? resolve(process.cwd(), entry) : entry,
33
+ ...(name && { name }),
34
+ formats: ['es'],
35
+ fileName: (_format, entryName) => `${entryName}.js`,
36
+ },
37
+ rollupOptions: {
38
+ ...viteConfig.build?.rollupOptions,
39
+ external: [...autoExternals, ...NODE_BUILTINS_EXTERNAL, ...userExternals],
40
+ output: {
41
+ preserveModules: false,
42
+ ...viteConfig.build?.rollupOptions?.output,
43
+ },
44
+ },
45
+ // Spread other build options from viteConfig except lib and rollupOptions
46
+ ...Object.fromEntries(Object.entries(viteConfig.build || {}).filter(([key]) => key !== 'lib' && key !== 'rollupOptions')),
47
+ },
48
+ // Spread other viteConfig options except build and plugins
49
+ ...Object.fromEntries(Object.entries(viteConfig).filter(([key]) => key !== 'build' && key !== 'plugins')),
50
+ });
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventuras/vite-config",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,13 +9,25 @@
9
9
  },
10
10
  "type": "module",
11
11
  "exports": {
12
- "./base": "./src/base.ts",
13
- "./react-lib": "./src/react-lib.ts",
14
- "./vanilla-lib": "./src/vanilla-lib.ts",
15
- "./next-lib": "./src/next-lib.ts"
12
+ "./base": {
13
+ "types": "./dist/base.d.ts",
14
+ "import": "./dist/base.js"
15
+ },
16
+ "./react-lib": {
17
+ "types": "./dist/react-lib.d.ts",
18
+ "import": "./dist/react-lib.js"
19
+ },
20
+ "./vanilla-lib": {
21
+ "types": "./dist/vanilla-lib.d.ts",
22
+ "import": "./dist/vanilla-lib.js"
23
+ },
24
+ "./next-lib": {
25
+ "types": "./dist/next-lib.d.ts",
26
+ "import": "./dist/next-lib.js"
27
+ }
16
28
  },
17
29
  "files": [
18
- "src"
30
+ "dist"
19
31
  ],
20
32
  "dependencies": {
21
33
  "@tailwindcss/vite": "^4.3.2",
@@ -25,13 +37,21 @@
25
37
  "vite-plugin-dts": "^5.0.3"
26
38
  },
27
39
  "devDependencies": {
40
+ "@types/node": "^24.12.0",
28
41
  "typescript": "^6.0.2",
29
- "vite": "^8.1.0"
42
+ "vite": "^8.1.0",
43
+ "@eventuras/typescript-config": "1.0.0"
30
44
  },
31
45
  "peerDependencies": {
32
46
  "vite": "^7.0.0 || ^8.0.0"
33
47
  },
34
48
  "publishConfig": {
35
49
  "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "tsc -p tsconfig.json",
53
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
54
+ "dev": "tsc -p tsconfig.json --watch",
55
+ "typecheck": "tsc -p tsconfig.json --noEmit"
36
56
  }
37
- }
57
+ }
package/src/base.ts DELETED
@@ -1,16 +0,0 @@
1
- import { defineConfig, type UserConfig } from 'vite';
2
-
3
- /**
4
- * Base Vite configuration with common defaults for all Eventuras packages.
5
- */
6
- export function defineBaseConfig(config: UserConfig = {}): UserConfig {
7
- return defineConfig({
8
- ...config,
9
- build: {
10
- ...config.build,
11
- // Common build optimizations
12
- minify: false, // Libraries should not be minified
13
- sourcemap: true, // Always generate sourcemaps for debugging
14
- },
15
- });
16
- }
package/src/next-lib.ts DELETED
@@ -1,36 +0,0 @@
1
- import type { UserConfig } from 'vite';
2
- import { defineReactLibConfig, type ReactLibConfig } from './react-lib.ts';
3
-
4
- export interface NextLibConfig extends Omit<ReactLibConfig, 'external'> {
5
- /**
6
- * Additional external dependencies beyond the Next.js defaults and the
7
- * package.json auto-externalization (via the underlying React preset).
8
- */
9
- external?: (string | RegExp)[];
10
- }
11
-
12
- /**
13
- * Vite configuration preset for Next.js-compatible React libraries.
14
- * Extends the React library config with Next.js-specific externals and defaults.
15
- */
16
- export function defineNextLibConfig(config: NextLibConfig): UserConfig {
17
- const { external = [], ...reactConfig } = config;
18
-
19
- // Next.js externals - include all next/* paths to avoid bundling internal modules
20
- const nextExternals = [
21
- 'next',
22
- 'next/image',
23
- 'next/link',
24
- 'next/navigation',
25
- 'next/router',
26
- 'next/headers',
27
- /^next\//, // Externalize all next/* imports
28
- ];
29
-
30
- return defineReactLibConfig({
31
- ...reactConfig,
32
- external: [...nextExternals, ...external],
33
- // Next.js libraries should always preserve client directives
34
- preserveUseClientDirectives: true,
35
- });
36
- }
package/src/react-lib.ts DELETED
@@ -1,239 +0,0 @@
1
- import { defineConfig, type UserConfig } from 'vite';
2
- import react from '@vitejs/plugin-react';
3
- import tailwindcss from '@tailwindcss/vite';
4
- import dts from 'vite-plugin-dts';
5
- import { resolve } from 'node:path';
6
- import { glob } from 'glob';
7
- import fs from 'node:fs';
8
-
9
- import { getRuntimeDependencyExternals, NODE_BUILTINS_EXTERNAL } from './externals.ts';
10
-
11
- /**
12
- * Plugin to preserve 'use client' directives in React Server Components.
13
- * This ensures client-side code is properly marked when building for Next.js.
14
- */
15
- function preserveUseClient() {
16
- return {
17
- name: 'preserve-use-client',
18
- enforce: 'post' as const,
19
- generateBundle(_options: any, bundle: any) {
20
- for (const [_fileName, chunk] of Object.entries(bundle)) {
21
- if ((chunk as any).type === 'chunk') {
22
- const chunkData = chunk as any;
23
- // Check if any source module had 'use client'
24
- const hasClientDirective = chunkData.moduleIds?.some((id: string) => {
25
- try {
26
- const content = fs.readFileSync(id, 'utf-8');
27
- // Remove leading comments to check for 'use client'
28
- const withoutComments = content
29
- .replace(/^(\s*\/\/.*\n)+/, '')
30
- .replace(/^(\s*\/\*[\s\S]*?\*\/\s*)/, '');
31
- return (
32
- withoutComments.trimStart().startsWith("'use client'") ||
33
- withoutComments.trimStart().startsWith('"use client"')
34
- );
35
- } catch {
36
- return false;
37
- }
38
- });
39
-
40
- if (hasClientDirective) {
41
- const codeStart = chunkData.code.trimStart();
42
- if (!codeStart.startsWith("'use client'") && !codeStart.startsWith('"use client"')) {
43
- chunkData.code = `'use client';\n${chunkData.code}`;
44
- }
45
- }
46
- }
47
- }
48
- },
49
- };
50
- }
51
-
52
- export interface ReactLibConfig {
53
- /**
54
- * Library entry point(s). Can be:
55
- * - A string path: 'src/index.ts'
56
- * - An object with multiple entries: { index: 'src/index.ts', utils: 'src/utils.ts' }
57
- * - A glob pattern: 'src/** /index.ts' (auto-discovers multiple entry points)
58
- */
59
- entry: string | Record<string, string>;
60
-
61
- /**
62
- * Additional external dependencies to exclude from the bundle, on top of the
63
- * runtime deps read from the consumer's package.json and the React core
64
- * externals. Pass `false` to opt out of package.json auto-externalization.
65
- */
66
- external?: (string | RegExp)[] | false;
67
-
68
- /**
69
- * Enable Tailwind CSS support via @tailwindcss/vite plugin.
70
- * @default false
71
- */
72
- tailwind?: boolean;
73
-
74
- /**
75
- * Preserve module structure in output.
76
- * When true, creates a dist structure mirroring src structure.
77
- * When false, bundles everything into fewer files.
78
- * @default true
79
- */
80
- preserveModules?: boolean;
81
-
82
- /**
83
- * Enable 'use client' directive preservation for Next.js React Server Components.
84
- * @default true
85
- */
86
- preserveUseClientDirectives?: boolean;
87
-
88
- /**
89
- * Use SWC instead of Babel for React transformation (faster builds).
90
- * @default false
91
- */
92
- useSWC?: boolean;
93
-
94
- /**
95
- * TypeScript declaration options.
96
- */
97
- dts?: {
98
- /**
99
- * Entry root for DTS generation.
100
- * @default 'src'
101
- */
102
- entryRoot?: string;
103
- /**
104
- * Output directory for .d.ts files.
105
- * @default 'dist'
106
- */
107
- outDir?: string;
108
- /**
109
- * Roll up types into a single .d.ts file.
110
- * @default false
111
- */
112
- rollupTypes?: boolean;
113
- };
114
-
115
- /**
116
- * Additional Vite configuration to merge.
117
- */
118
- viteConfig?: UserConfig;
119
- }
120
-
121
- /**
122
- * Vite configuration preset for React libraries.
123
- * Includes React plugin, TypeScript declarations, and common React externals.
124
- */
125
- export function defineReactLibConfig(config: ReactLibConfig): UserConfig {
126
- const {
127
- entry,
128
- external,
129
- tailwind = false,
130
- preserveModules = true,
131
- preserveUseClientDirectives = true,
132
- useSWC = false,
133
- dts: dtsOptions = {},
134
- viteConfig = {},
135
- } = config;
136
-
137
- const autoExternals =
138
- external === false ? [] : getRuntimeDependencyExternals();
139
- const userExternals = Array.isArray(external) ? external : [];
140
-
141
- // Determine entry points
142
- let entryPoints: string | string[] | Record<string, string>;
143
- if (typeof entry === 'string' && entry.includes('*')) {
144
- entryPoints = glob.sync(resolve(process.cwd(), entry));
145
- } else if (typeof entry === 'string') {
146
- entryPoints = resolve(process.cwd(), entry);
147
- } else {
148
- entryPoints = entry;
149
- }
150
-
151
- // Common React externals
152
- const reactExternals = ['react', 'react-dom', 'react/jsx-runtime'];
153
-
154
- const plugins: any[] = [];
155
-
156
- // Add React plugin (SWC or standard)
157
- if (useSWC) {
158
- // Dynamically import SWC plugin only when needed
159
- const reactSwc = require('@vitejs/plugin-react-swc').default;
160
- plugins.push(reactSwc());
161
- } else {
162
- plugins.push(react());
163
- }
164
-
165
- // Add Tailwind if enabled
166
- if (tailwind) {
167
- plugins.push(tailwindcss());
168
- }
169
-
170
- // Add DTS plugin
171
- plugins.push(
172
- dts({
173
- entryRoot: dtsOptions.entryRoot || 'src',
174
- outDir: dtsOptions.outDir || 'dist',
175
- include: ['src/**/*'],
176
- exclude: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx', '**/*.stories.tsx'],
177
- copyDtsFiles: true,
178
- rollupTypes: dtsOptions.rollupTypes || false,
179
- })
180
- );
181
-
182
- // Add use client preservation if enabled
183
- if (preserveUseClientDirectives) {
184
- plugins.push(preserveUseClient());
185
- }
186
-
187
- // Add custom plugins from viteConfig
188
- if (viteConfig.plugins) {
189
- plugins.push(...viteConfig.plugins);
190
- }
191
-
192
- return defineConfig({
193
- plugins,
194
- resolve: {
195
- alias: {
196
- '@': resolve(process.cwd(), './src'),
197
- ...viteConfig.resolve?.alias,
198
- },
199
- ...viteConfig.resolve,
200
- },
201
- build: {
202
- minify: false,
203
- sourcemap: true,
204
- lib: {
205
- entry: entryPoints,
206
- formats: ['es'],
207
- },
208
- rollupOptions: {
209
- ...viteConfig.build?.rollupOptions,
210
- external: [
211
- ...reactExternals,
212
- ...autoExternals,
213
- ...NODE_BUILTINS_EXTERNAL,
214
- ...userExternals,
215
- ],
216
- output: {
217
- preserveModules,
218
- ...(preserveModules && {
219
- preserveModulesRoot: 'src',
220
- entryFileNames: '[name].js',
221
- }),
222
- ...viteConfig.build?.rollupOptions?.output,
223
- },
224
- },
225
- // Spread other build options from viteConfig except lib and rollupOptions
226
- ...Object.fromEntries(
227
- Object.entries(viteConfig.build || {}).filter(
228
- ([key]) => key !== 'lib' && key !== 'rollupOptions'
229
- )
230
- ),
231
- },
232
- // Spread other viteConfig options except build, plugins, and resolve
233
- ...Object.fromEntries(
234
- Object.entries(viteConfig).filter(
235
- ([key]) => key !== 'build' && key !== 'plugins' && key !== 'resolve'
236
- )
237
- ),
238
- });
239
- }
@@ -1,90 +0,0 @@
1
- import { defineConfig, type UserConfig } from 'vite';
2
- import dts from 'vite-plugin-dts';
3
- import { resolve } from 'node:path';
4
-
5
- import { getRuntimeDependencyExternals, NODE_BUILTINS_EXTERNAL } from './externals.ts';
6
-
7
- export interface VanillaLibConfig {
8
- /**
9
- * Library entry point(s). Can be a string or an object with multiple entries.
10
- * @example 'src/index.ts'
11
- * @example { index: 'src/index.ts', utils: 'src/utils.ts' }
12
- */
13
- entry: string | Record<string, string>;
14
-
15
- /**
16
- * Library name for UMD builds (optional).
17
- */
18
- name?: string;
19
-
20
- /**
21
- * Additional external dependencies to exclude from the bundle, on top of the
22
- * runtime deps read from the consumer's package.json. Pass `false` to opt out
23
- * of the package.json auto-externalization (rarely what you want).
24
- */
25
- external?: (string | RegExp)[] | false;
26
-
27
- /**
28
- * Additional Vite configuration to merge.
29
- */
30
- viteConfig?: UserConfig;
31
- }
32
-
33
- /**
34
- * Vite configuration preset for vanilla TypeScript libraries.
35
- *
36
- * Defaults that matter for libraries:
37
- * - All runtime deps (dependencies + peerDependencies + optionalDependencies)
38
- * are externalized so consumers get one copy from their node_modules instead
39
- * of inlined+mangled copies inside the published bundle.
40
- * - Minification is OFF — consumers minify their own bundle, and unminified
41
- * output preserves class names (so `instanceof` and stack traces work).
42
- * - Sourcemaps ON for debuggable consumer stack traces.
43
- */
44
- export function defineVanillaLibConfig(config: VanillaLibConfig): UserConfig {
45
- const { entry, name, external, viteConfig = {} } = config;
46
-
47
- const autoExternals =
48
- external === false ? [] : getRuntimeDependencyExternals();
49
- const userExternals = Array.isArray(external) ? external : [];
50
-
51
- return defineConfig({
52
- plugins: [
53
- dts({
54
- include: ['src/**/*'],
55
- exclude: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
56
- }),
57
- ...(viteConfig.plugins || []),
58
- ],
59
- build: {
60
- minify: false,
61
- sourcemap: true,
62
- lib: {
63
- entry: typeof entry === 'string' ? resolve(process.cwd(), entry) : entry,
64
- ...(name && { name }),
65
- formats: ['es'],
66
- fileName: (_format, entryName) => `${entryName}.js`,
67
- },
68
- rollupOptions: {
69
- ...viteConfig.build?.rollupOptions,
70
- external: [...autoExternals, ...NODE_BUILTINS_EXTERNAL, ...userExternals],
71
- output: {
72
- preserveModules: false,
73
- ...viteConfig.build?.rollupOptions?.output,
74
- },
75
- },
76
- // Spread other build options from viteConfig except lib and rollupOptions
77
- ...Object.fromEntries(
78
- Object.entries(viteConfig.build || {}).filter(
79
- ([key]) => key !== 'lib' && key !== 'rollupOptions'
80
- )
81
- ),
82
- },
83
- // Spread other viteConfig options except build and plugins
84
- ...Object.fromEntries(
85
- Object.entries(viteConfig).filter(
86
- ([key]) => key !== 'build' && key !== 'plugins'
87
- )
88
- ),
89
- });
90
- }