next-vite-router 1.0.3 → 1.0.5
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 +87 -12
- package/dist/cli.cjs +527 -0
- package/dist/index.js +53 -48
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Next Vite Router is an npm package that provides Next.js-style, file-based routing for React applications built with Vite. It generates React Router route objects from a filesystem layout (pages, layouts, not-found), and it exposes a Vite plugin that injects a virtual module to generate routes at build/dev time.
|
|
4
4
|
|
|
5
|
-
This README focuses on how consumers install and use the package (no publishing instructions).
|
|
6
|
-
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
Install with your package manager of choice:
|
|
@@ -18,6 +16,51 @@ yarn add next-vite-router
|
|
|
18
16
|
|
|
19
17
|
The package has peer dependencies: React, React DOM and React Router DOM. Make sure your project already includes compatible versions (React 18+ is recommended).
|
|
20
18
|
|
|
19
|
+
## CLI / Getting Started
|
|
20
|
+
|
|
21
|
+
The quickest way to start a new project with `next-vite-router` is to use the interactive CLI:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx next-vite-router init
|
|
25
|
+
# or
|
|
26
|
+
pnpm dlx next-vite-router init
|
|
27
|
+
# or
|
|
28
|
+
yarn dlx next-vite-router init
|
|
29
|
+
# or
|
|
30
|
+
bunx next-vite-router init
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The CLI will prompt you for:
|
|
34
|
+
|
|
35
|
+
1. **Project name** name of the folder to create
|
|
36
|
+
2. **Template** which React Vite variant to use:
|
|
37
|
+
- `react-ts` (React + TypeScript)
|
|
38
|
+
- `react-swc-ts` (React + SWC + TypeScript)
|
|
39
|
+
- `react-compiler-ts` (React Compiler + TypeScript)
|
|
40
|
+
3. **Src folder** whether to keep source files in `src/` (recommended) or place them at the project root
|
|
41
|
+
4. **Tailwind CSS** whether to add and configure Tailwind CSS (with `@tailwindcss/vite`)
|
|
42
|
+
|
|
43
|
+
### What the CLI does
|
|
44
|
+
|
|
45
|
+
The CLI automates the entire setup:
|
|
46
|
+
|
|
47
|
+
- Scaffolds a new Vite + React project using `create-vite`
|
|
48
|
+
- Installs `next-vite-router` and `react-router-dom@latest`
|
|
49
|
+
- Configures the Vite plugin in `vite.config.ts`
|
|
50
|
+
- Sets up path aliases (`@/*`) in `tsconfig.json` and Vite config
|
|
51
|
+
- Creates TypeScript declarations for the virtual module (`virtual:next-vite-router`)
|
|
52
|
+
- Generates example pages (`app/page.tsx`, `app/layout.tsx`, `app/example/page.tsx`)
|
|
53
|
+
- Optionally configures Tailwind CSS with `@tailwindcss/vite` and creates `global.css`
|
|
54
|
+
|
|
55
|
+
After the CLI finishes:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd <your-project-name>
|
|
59
|
+
pnpm dev
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Your app is ready with file-based routing!
|
|
63
|
+
|
|
21
64
|
## Quick Start Vite plugin
|
|
22
65
|
|
|
23
66
|
The recommended and easiest way to use this library in a Vite app is via the Vite plugin. The plugin exposes a virtual module `virtual:next-vite-router` that provides a `generateRoutes()` function and re-exports `useRoutes` from `react-router-dom` for convenience.
|
|
@@ -37,28 +80,60 @@ export default defineConfig({
|
|
|
37
80
|
});
|
|
38
81
|
```
|
|
39
82
|
|
|
40
|
-
Example usage inside your app (
|
|
83
|
+
Example usage inside your app. **Important: generate routes once at module level** (not inside the component) to avoid infinite re-renders.
|
|
84
|
+
|
|
85
|
+
File: `src/App.tsx`
|
|
41
86
|
|
|
42
87
|
```tsx
|
|
43
|
-
import
|
|
44
|
-
import {
|
|
45
|
-
import { generateRoutes, useRoutes } from 'virtual:next-vite-router';
|
|
88
|
+
import { useRoutes } from 'react-router-dom';
|
|
89
|
+
import { generateRoutes } from 'virtual:next-vite-router';
|
|
46
90
|
|
|
47
91
|
const routes = generateRoutes();
|
|
48
92
|
|
|
49
|
-
function App() {
|
|
93
|
+
export default function App() {
|
|
50
94
|
const element = useRoutes(routes);
|
|
51
|
-
return
|
|
95
|
+
return element;
|
|
52
96
|
}
|
|
97
|
+
```
|
|
53
98
|
|
|
54
|
-
|
|
99
|
+
File: `src/main.tsx`
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { StrictMode } from 'react';
|
|
103
|
+
import { createRoot } from 'react-dom/client';
|
|
104
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
105
|
+
import App from './App';
|
|
106
|
+
|
|
107
|
+
createRoot(document.getElementById('root')!).render(
|
|
108
|
+
<StrictMode>
|
|
109
|
+
<BrowserRouter>
|
|
110
|
+
<App />
|
|
111
|
+
</BrowserRouter>
|
|
112
|
+
</StrictMode>
|
|
113
|
+
);
|
|
55
114
|
```
|
|
56
115
|
|
|
57
|
-
|
|
116
|
+
**Key points:**
|
|
58
117
|
|
|
59
|
-
-
|
|
118
|
+
- Call `generateRoutes()` once at module level, not inside the component (prevents infinite re-renders).
|
|
119
|
+
- Wrap your app with `BrowserRouter` at the entry point (`main.tsx`), not inside `App`.
|
|
120
|
+
- `pagesDir` (default: `src/app`) is the directory where you organize your **route files** (`page.tsx`, `layout.tsx`, `not-found.tsx`). It has nothing to do with the `App.tsx` component.
|
|
60
121
|
- The virtual module is `virtual:next-vite-router` (the plugin resolves it internally).
|
|
61
122
|
|
|
123
|
+
**Example file structure:**
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
src/
|
|
127
|
+
├── App.tsx ← Your main app component
|
|
128
|
+
├── main.tsx ← Entry point (with BrowserRouter)
|
|
129
|
+
└── app/ ← pagesDir (routes are organized here)
|
|
130
|
+
├── page.tsx ← Home page
|
|
131
|
+
├── layout.tsx ← Root layout
|
|
132
|
+
└── dashboard/
|
|
133
|
+
├── page.tsx ← /dashboard page
|
|
134
|
+
└── layout.tsx ← Dashboard layout
|
|
135
|
+
```
|
|
136
|
+
|
|
62
137
|
TypeScript note:
|
|
63
138
|
|
|
64
139
|
If you're using TypeScript in a consuming project, add a declaration file so the virtual module is recognized by the compiler. Create a file such as `src/virtual.d.ts` (or any `*.d.ts` included by your `tsconfig.json`) with the following content:
|
|
@@ -170,4 +245,4 @@ Pass a custom `pagesDir` if your project uses `src/pages` or another convention.
|
|
|
170
245
|
|
|
171
246
|
## License
|
|
172
247
|
|
|
173
|
-
MIT
|
|
248
|
+
MIT
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const commander = require("commander");
|
|
3
|
+
const node_child_process = require("node:child_process");
|
|
4
|
+
const child_process = require("child_process");
|
|
5
|
+
const prompts = require("prompts");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
function isAvailable(cmd) {
|
|
9
|
+
try {
|
|
10
|
+
node_child_process.execSync(`${cmd} --version`, { stdio: "ignore" });
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function detectFromUserAgent() {
|
|
17
|
+
const ua = process.env.npm_config_user_agent;
|
|
18
|
+
if (!ua) return null;
|
|
19
|
+
if (ua.startsWith("pnpm")) return "pnpm";
|
|
20
|
+
if (ua.startsWith("bun")) return "bun";
|
|
21
|
+
if (ua.startsWith("yarn")) return "yarn";
|
|
22
|
+
if (ua.startsWith("npm")) return "npm";
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function detectPackageManager() {
|
|
26
|
+
const preferred = process.env.PREFERRED_PM ?? void 0;
|
|
27
|
+
const fromUA = detectFromUserAgent();
|
|
28
|
+
const candidates = [];
|
|
29
|
+
if (fromUA) candidates.push(fromUA);
|
|
30
|
+
if (preferred && !candidates.includes(preferred)) candidates.push(preferred);
|
|
31
|
+
["pnpm", "bun", "yarn", "npm"].forEach((p) => {
|
|
32
|
+
const pm2 = p;
|
|
33
|
+
if (!candidates.includes(pm2)) candidates.push(pm2);
|
|
34
|
+
});
|
|
35
|
+
let chosen = null;
|
|
36
|
+
for (const pm2 of candidates) {
|
|
37
|
+
if (isAvailable(pm2)) {
|
|
38
|
+
chosen = pm2;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!chosen) {
|
|
43
|
+
chosen = "npm";
|
|
44
|
+
}
|
|
45
|
+
switch (chosen) {
|
|
46
|
+
case "pnpm":
|
|
47
|
+
return {
|
|
48
|
+
name: "pnpm",
|
|
49
|
+
createProjectCommand: (projectName, template) => `pnpm create vite@latest ${projectName} --template ${template} --no-rolldown`,
|
|
50
|
+
addDepsCommand: (deps) => `pnpm add ${deps.join(" ")}`,
|
|
51
|
+
addDevDepsCommand: (deps) => `pnpm add -D ${deps.join(" ")}`,
|
|
52
|
+
installCommand: "pnpm install"
|
|
53
|
+
};
|
|
54
|
+
case "bun":
|
|
55
|
+
return {
|
|
56
|
+
name: "bun",
|
|
57
|
+
createProjectCommand: (projectName, template) => `bun create vite ${projectName} --template=${template} --no-rolldown`,
|
|
58
|
+
addDepsCommand: (deps) => `bun add ${deps.join(" ")}`,
|
|
59
|
+
addDevDepsCommand: (deps) => `bun add -d ${deps.join(" ")}`,
|
|
60
|
+
installCommand: "bun install"
|
|
61
|
+
};
|
|
62
|
+
case "yarn":
|
|
63
|
+
return {
|
|
64
|
+
name: "yarn",
|
|
65
|
+
createProjectCommand: (projectName, template) => `yarn create vite ${projectName} --template=${template} --no-rolldown`,
|
|
66
|
+
addDepsCommand: (deps) => `yarn add ${deps.join(" ")}`,
|
|
67
|
+
addDevDepsCommand: (deps) => `yarn add -D ${deps.join(" ")}`,
|
|
68
|
+
installCommand: "yarn install"
|
|
69
|
+
};
|
|
70
|
+
case "npm":
|
|
71
|
+
default:
|
|
72
|
+
return {
|
|
73
|
+
name: "npm",
|
|
74
|
+
createProjectCommand: (projectName, template) => `npm create vite@latest ${projectName} -- --template ${template} --no-rolldown`,
|
|
75
|
+
addDepsCommand: (deps) => `npm install ${deps.join(" ")}`,
|
|
76
|
+
addDevDepsCommand: (deps) => `npm install -D ${deps.join(" ")}`,
|
|
77
|
+
installCommand: "npm install"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM, isTTY = true;
|
|
82
|
+
if (typeof process !== "undefined") {
|
|
83
|
+
({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {});
|
|
84
|
+
isTTY = process.stdout && process.stdout.isTTY;
|
|
85
|
+
}
|
|
86
|
+
const $ = {
|
|
87
|
+
enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY)
|
|
88
|
+
};
|
|
89
|
+
function init$1(x, y) {
|
|
90
|
+
let rgx = new RegExp(`\\x1b\\[${y}m`, "g");
|
|
91
|
+
let open = `\x1B[${x}m`, close = `\x1B[${y}m`;
|
|
92
|
+
return function(txt) {
|
|
93
|
+
if (!$.enabled || txt == null) return txt;
|
|
94
|
+
return open + (!!~("" + txt).indexOf(close) ? txt.replace(rgx, close + open) : txt) + close;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const red = init$1(31, 39);
|
|
98
|
+
const green = init$1(32, 39);
|
|
99
|
+
const yellow = init$1(33, 39);
|
|
100
|
+
const cyan = init$1(36, 39);
|
|
101
|
+
const highlighter = {
|
|
102
|
+
error: red,
|
|
103
|
+
warn: yellow,
|
|
104
|
+
info: cyan,
|
|
105
|
+
success: green
|
|
106
|
+
};
|
|
107
|
+
const logger = {
|
|
108
|
+
error(...args) {
|
|
109
|
+
console.log(highlighter.error(args.join(" ")));
|
|
110
|
+
},
|
|
111
|
+
warn(...args) {
|
|
112
|
+
console.log(highlighter.warn(args.join(" ")));
|
|
113
|
+
},
|
|
114
|
+
info(...args) {
|
|
115
|
+
console.log(highlighter.info(args.join(" ")));
|
|
116
|
+
},
|
|
117
|
+
success(...args) {
|
|
118
|
+
console.log(highlighter.success(args.join(" ")));
|
|
119
|
+
},
|
|
120
|
+
log(...args) {
|
|
121
|
+
console.log(args.join(" "));
|
|
122
|
+
},
|
|
123
|
+
break() {
|
|
124
|
+
console.log("");
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
function getViteConfigPath(projectDir) {
|
|
128
|
+
const tsPath = path.join(projectDir, "vite.config.ts");
|
|
129
|
+
const jsPath = path.join(projectDir, "vite.config.js");
|
|
130
|
+
if (fs.existsSync(tsPath)) return tsPath;
|
|
131
|
+
if (fs.existsSync(jsPath)) return jsPath;
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
function readViteConfig(projectDir) {
|
|
135
|
+
const configPath = getViteConfigPath(projectDir);
|
|
136
|
+
if (!configPath) return null;
|
|
137
|
+
return fs.readFileSync(configPath, "utf-8");
|
|
138
|
+
}
|
|
139
|
+
function writeViteConfig(projectDir, content) {
|
|
140
|
+
const configPath = getViteConfigPath(projectDir);
|
|
141
|
+
if (!configPath) throw new Error("Vite config not found");
|
|
142
|
+
fs.writeFileSync(configPath, content, "utf-8");
|
|
143
|
+
}
|
|
144
|
+
function stripJsonComments(jsonString) {
|
|
145
|
+
jsonString = jsonString.replace(/\/\/.*$/gm, "");
|
|
146
|
+
jsonString = jsonString.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
147
|
+
jsonString = jsonString.replace(/,(\s*[}\]])/g, "$1");
|
|
148
|
+
return jsonString;
|
|
149
|
+
}
|
|
150
|
+
function updateAppTsConfig(projectDir) {
|
|
151
|
+
const tsConfigAppPath = path.join(projectDir, "tsconfig.app.json");
|
|
152
|
+
if (!fs.existsSync(tsConfigAppPath)) return;
|
|
153
|
+
const content = fs.readFileSync(tsConfigAppPath, "utf-8");
|
|
154
|
+
const cleanedContent = stripJsonComments(content);
|
|
155
|
+
const tsConfigApp = JSON.parse(cleanedContent);
|
|
156
|
+
tsConfigApp.include = [
|
|
157
|
+
"./**/*.ts",
|
|
158
|
+
"./**/*.tsx",
|
|
159
|
+
"virtual-next-vite-router.d.ts"
|
|
160
|
+
];
|
|
161
|
+
fs.writeFileSync(
|
|
162
|
+
tsConfigAppPath,
|
|
163
|
+
JSON.stringify(tsConfigApp, null, 2),
|
|
164
|
+
"utf-8"
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function updateTsConfigJson(projectDir, src) {
|
|
168
|
+
const tsConfigPath = path.join(projectDir, "tsconfig.json");
|
|
169
|
+
if (!fs.existsSync(tsConfigPath)) return;
|
|
170
|
+
const content = fs.readFileSync(tsConfigPath, "utf-8");
|
|
171
|
+
const cleanedContent = stripJsonComments(content);
|
|
172
|
+
const tsConfig = JSON.parse(cleanedContent);
|
|
173
|
+
if (!tsConfig.compilerOptions) {
|
|
174
|
+
tsConfig.compilerOptions = {};
|
|
175
|
+
}
|
|
176
|
+
tsConfig.compilerOptions.baseUrl = ".";
|
|
177
|
+
tsConfig.compilerOptions.paths = {
|
|
178
|
+
"@/*": [src ? "./src/*" : "./*"]
|
|
179
|
+
};
|
|
180
|
+
fs.writeFileSync(tsConfigPath, JSON.stringify(tsConfig, null, 2), "utf-8");
|
|
181
|
+
}
|
|
182
|
+
function createVirtualDTS(projectDir, useSrc) {
|
|
183
|
+
const targetDir = useSrc ? path.join(projectDir, "src") : projectDir;
|
|
184
|
+
if (useSrc && !fs.existsSync(targetDir)) {
|
|
185
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
const filePath = path.join(targetDir, "virtual-next-vite-router.d.ts");
|
|
188
|
+
const content = `declare module 'virtual:next-vite-router' {
|
|
189
|
+
import type { RouteObject } from 'react-router-dom';
|
|
190
|
+
|
|
191
|
+
export const generateRoutes: () => RouteObject[];
|
|
192
|
+
export { useRoutes } from 'react-router-dom';
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
196
|
+
}
|
|
197
|
+
function createMainTSX(projectDir, useSrc) {
|
|
198
|
+
const targetDir = useSrc ? path.join(projectDir, "src") : projectDir;
|
|
199
|
+
if (useSrc && !fs.existsSync(targetDir)) {
|
|
200
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
201
|
+
}
|
|
202
|
+
const mainPath = path.join(targetDir, "main.tsx");
|
|
203
|
+
const content = `import { StrictMode } from 'react';
|
|
204
|
+
import { createRoot } from 'react-dom/client';
|
|
205
|
+
import { BrowserRouter } from "react-router-dom";
|
|
206
|
+
import { generateRoutes, useRoutes } from 'virtual:next-vite-router';
|
|
207
|
+
|
|
208
|
+
const routes = generateRoutes();
|
|
209
|
+
|
|
210
|
+
function App() {
|
|
211
|
+
const element = useRoutes(routes);
|
|
212
|
+
return element;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
createRoot(document.getElementById('root')!).render(
|
|
216
|
+
<StrictMode>
|
|
217
|
+
<BrowserRouter>
|
|
218
|
+
<App />
|
|
219
|
+
</BrowserRouter>
|
|
220
|
+
</StrictMode>,
|
|
221
|
+
);
|
|
222
|
+
`;
|
|
223
|
+
fs.writeFileSync(mainPath, content, "utf-8");
|
|
224
|
+
}
|
|
225
|
+
function createGlobalsCss({ projectDir, src, tailwind }) {
|
|
226
|
+
const cssDir = src ? path.join(projectDir, "src") : projectDir;
|
|
227
|
+
const cssPath = path.join(cssDir, "globals.css");
|
|
228
|
+
if (!fs.existsSync(cssDir)) {
|
|
229
|
+
fs.mkdirSync(cssDir, { recursive: true });
|
|
230
|
+
}
|
|
231
|
+
let content;
|
|
232
|
+
if (tailwind) {
|
|
233
|
+
content = '@import "tailwindcss";\n';
|
|
234
|
+
} else {
|
|
235
|
+
content = `* {
|
|
236
|
+
margin: 0;
|
|
237
|
+
padding: 0;
|
|
238
|
+
box-sizing: border-box;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
body {
|
|
242
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
|
243
|
+
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
|
244
|
+
sans-serif;
|
|
245
|
+
-webkit-font-smoothing: antialiased;
|
|
246
|
+
-moz-osx-font-smoothing: grayscale;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
html,
|
|
250
|
+
body,
|
|
251
|
+
#root {
|
|
252
|
+
width: 100%;
|
|
253
|
+
height: 100%;
|
|
254
|
+
}
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
fs.writeFileSync(cssPath, content, "utf-8");
|
|
258
|
+
}
|
|
259
|
+
const layoutContentNoTailwind = `import { Outlet } from "react-router-dom";
|
|
260
|
+
import "@/globals.css";
|
|
261
|
+
|
|
262
|
+
export default function Layout() {
|
|
263
|
+
return (
|
|
264
|
+
<main>
|
|
265
|
+
<Outlet />
|
|
266
|
+
</main>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
`;
|
|
270
|
+
const layoutContentTailwind = `import { Outlet } from "react-router-dom";
|
|
271
|
+
import "@/globals.css";
|
|
272
|
+
|
|
273
|
+
export default function Layout() {
|
|
274
|
+
return (
|
|
275
|
+
<main className="min-h-screen bg-background">
|
|
276
|
+
<Outlet />
|
|
277
|
+
</main>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
`;
|
|
281
|
+
const pageContentNoTailwind = `export default function Page() {
|
|
282
|
+
return <h1>Welcome to the next-vite-router project!</h1>;
|
|
283
|
+
}
|
|
284
|
+
`;
|
|
285
|
+
const pageContentTailwind = `export default function Page() {
|
|
286
|
+
return <h1 className="text-2xl font-bold">Welcome to the next-vite-router project!</h1>;
|
|
287
|
+
}
|
|
288
|
+
`;
|
|
289
|
+
const examplePageContent = `export default function ExamplePage() {
|
|
290
|
+
return <h1>Welcome to the example page!</h1>;
|
|
291
|
+
}
|
|
292
|
+
`;
|
|
293
|
+
function createExampleStructure(projectDir, src, tailwindcss) {
|
|
294
|
+
const appDir = src ? path.join(projectDir, "src", "app") : path.join(projectDir, "app");
|
|
295
|
+
if (!fs.existsSync(appDir)) {
|
|
296
|
+
fs.mkdirSync(appDir, { recursive: true });
|
|
297
|
+
}
|
|
298
|
+
const layoutContent = tailwindcss ? layoutContentTailwind : layoutContentNoTailwind;
|
|
299
|
+
fs.writeFileSync(path.join(appDir, "layout.tsx"), layoutContent, "utf-8");
|
|
300
|
+
const pageContent = tailwindcss ? pageContentTailwind : pageContentNoTailwind;
|
|
301
|
+
fs.writeFileSync(path.join(appDir, "page.tsx"), pageContent, "utf-8");
|
|
302
|
+
const exampleDir = path.join(appDir, "example");
|
|
303
|
+
if (!fs.existsSync(exampleDir)) {
|
|
304
|
+
fs.mkdirSync(exampleDir, { recursive: true });
|
|
305
|
+
}
|
|
306
|
+
fs.writeFileSync(
|
|
307
|
+
path.join(exampleDir, "page.tsx"),
|
|
308
|
+
examplePageContent,
|
|
309
|
+
"utf-8"
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
function addTailwindToViteConfig(projectDir) {
|
|
313
|
+
const content = readViteConfig(projectDir);
|
|
314
|
+
if (!content) throw new Error("Vite config not found");
|
|
315
|
+
if (content.includes("@tailwindcss/vite")) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
let updatedContent = content;
|
|
319
|
+
const importRegex = /(import\s+.*from\s+['"].*['"];?\s*\n)+/;
|
|
320
|
+
const match = updatedContent.match(importRegex);
|
|
321
|
+
if (match) {
|
|
322
|
+
const lastImportEnd = match[0].length;
|
|
323
|
+
updatedContent = updatedContent.slice(0, lastImportEnd) + "import tailwindcss from '@tailwindcss/vite';\n" + updatedContent.slice(lastImportEnd);
|
|
324
|
+
} else {
|
|
325
|
+
updatedContent = "import tailwindcss from '@tailwindcss/vite';\n\n" + updatedContent;
|
|
326
|
+
}
|
|
327
|
+
const pluginsArrayRegex = /plugins:\s*\[([^\]]*)\]/s;
|
|
328
|
+
const pluginsMatch = updatedContent.match(pluginsArrayRegex);
|
|
329
|
+
if (pluginsMatch) {
|
|
330
|
+
const pluginsContent = pluginsMatch[1];
|
|
331
|
+
const newPluginsContent = pluginsContent.trim() ? pluginsContent.trimEnd() + ",\n tailwindcss()" : "\n tailwindcss()\n ";
|
|
332
|
+
updatedContent = updatedContent.replace(
|
|
333
|
+
pluginsArrayRegex,
|
|
334
|
+
`plugins: [${newPluginsContent}
|
|
335
|
+
]`
|
|
336
|
+
);
|
|
337
|
+
} else {
|
|
338
|
+
const defineConfigRegex = /defineConfig\(\{/;
|
|
339
|
+
updatedContent = updatedContent.replace(
|
|
340
|
+
defineConfigRegex,
|
|
341
|
+
"defineConfig({\n plugins: [tailwindcss()],"
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
writeViteConfig(projectDir, updatedContent);
|
|
345
|
+
}
|
|
346
|
+
const pm$1 = detectPackageManager();
|
|
347
|
+
function startNextViteRouterConfig({
|
|
348
|
+
projectDir = process.cwd(),
|
|
349
|
+
src,
|
|
350
|
+
tailwindcss
|
|
351
|
+
}) {
|
|
352
|
+
startViteConfig(projectDir, src);
|
|
353
|
+
const srcPath = path.join(projectDir, "src");
|
|
354
|
+
if (fs.existsSync(srcPath)) {
|
|
355
|
+
fs.rmSync(srcPath, { recursive: true, force: true });
|
|
356
|
+
}
|
|
357
|
+
updateTsConfigJson(projectDir, src);
|
|
358
|
+
if (!src) {
|
|
359
|
+
updateAppTsConfig(projectDir);
|
|
360
|
+
}
|
|
361
|
+
createVirtualDTS(projectDir, src);
|
|
362
|
+
createMainTSX(projectDir, src);
|
|
363
|
+
if (tailwindcss) {
|
|
364
|
+
logger.info("Setting up Tailwind CSS...");
|
|
365
|
+
const addDevDepsCmd = pm$1.addDevDepsCommand([
|
|
366
|
+
"tailwindcss",
|
|
367
|
+
"@tailwindcss/vite"
|
|
368
|
+
]);
|
|
369
|
+
child_process.execSync(addDevDepsCmd, { stdio: "inherit", cwd: projectDir });
|
|
370
|
+
addTailwindToViteConfig(projectDir);
|
|
371
|
+
}
|
|
372
|
+
createGlobalsCss({ projectDir, src, tailwind: tailwindcss });
|
|
373
|
+
createExampleStructure(projectDir, src, tailwindcss);
|
|
374
|
+
}
|
|
375
|
+
function startViteConfig(projectDir, src) {
|
|
376
|
+
const viteConfigPath = getViteConfigPath(projectDir);
|
|
377
|
+
if (!viteConfigPath) throw new Error("Vite config not found");
|
|
378
|
+
let viteConfigContent = readViteConfig(projectDir);
|
|
379
|
+
if (!viteConfigContent) throw new Error("Vite config empty");
|
|
380
|
+
if (!viteConfigContent.includes("import { resolve }")) {
|
|
381
|
+
viteConfigContent = `import { resolve } from "path";
|
|
382
|
+
` + viteConfigContent;
|
|
383
|
+
}
|
|
384
|
+
if (!viteConfigContent.includes("import { nextViteRouter }")) {
|
|
385
|
+
viteConfigContent = `import { nextViteRouter } from "next-vite-router/plugin";
|
|
386
|
+
` + viteConfigContent;
|
|
387
|
+
}
|
|
388
|
+
const resolveConfig = ` resolve: {
|
|
389
|
+
alias: {
|
|
390
|
+
"@": resolve(__dirname, "./${src ? "src" : "."}"),
|
|
391
|
+
},
|
|
392
|
+
},`;
|
|
393
|
+
const defineConfigRegex = /defineConfig\(\s*{/;
|
|
394
|
+
if (!viteConfigContent.includes("resolve:")) {
|
|
395
|
+
viteConfigContent = viteConfigContent.replace(
|
|
396
|
+
defineConfigRegex,
|
|
397
|
+
`defineConfig({
|
|
398
|
+
${resolveConfig}
|
|
399
|
+
`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
const pluginsRegex = /plugins\s*:\s*\[(.*?)\]/s;
|
|
403
|
+
const match = viteConfigContent.match(pluginsRegex);
|
|
404
|
+
const pluginString = `nextViteRouter({ pagesDir: "${src ? "src/app" : "app"}", pageFile: "page.tsx", layoutFile: "layout.tsx", notFoundFile: "not-found.tsx" })`;
|
|
405
|
+
if (match) {
|
|
406
|
+
const existing = match[1].trim();
|
|
407
|
+
const newPlugins = existing ? `${existing}, ${pluginString}` : pluginString;
|
|
408
|
+
viteConfigContent = viteConfigContent.replace(
|
|
409
|
+
pluginsRegex,
|
|
410
|
+
`plugins: [${newPlugins}]`
|
|
411
|
+
);
|
|
412
|
+
} else {
|
|
413
|
+
viteConfigContent = viteConfigContent.replace(
|
|
414
|
+
/defineConfig\(\s*{/,
|
|
415
|
+
`defineConfig({
|
|
416
|
+
plugins: [${pluginString}],`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
writeViteConfig(projectDir, viteConfigContent);
|
|
420
|
+
}
|
|
421
|
+
const pm = detectPackageManager();
|
|
422
|
+
const init = new commander.Command().name("init").description(
|
|
423
|
+
"Initialize a new Vite project with next-vite-router configuration"
|
|
424
|
+
).action(async () => {
|
|
425
|
+
const projectNameResponse = await prompts(
|
|
426
|
+
{
|
|
427
|
+
type: "text",
|
|
428
|
+
name: "projectName",
|
|
429
|
+
message: "Enter your project name:",
|
|
430
|
+
initial: "my-next-vite-router-app"
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
onCancel: () => {
|
|
434
|
+
logger.error("Project creation cancelled.");
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
const projectName = projectNameResponse.projectName;
|
|
440
|
+
const variantResponse = await prompts(
|
|
441
|
+
{
|
|
442
|
+
type: "select",
|
|
443
|
+
name: "template",
|
|
444
|
+
message: "Select a template:",
|
|
445
|
+
choices: [
|
|
446
|
+
{ title: "React + TypeScript", value: "react-ts" },
|
|
447
|
+
{
|
|
448
|
+
title: "React + TypeScript (React Compiler)",
|
|
449
|
+
value: "react-compiler-ts"
|
|
450
|
+
},
|
|
451
|
+
{ title: "React + SWC (Recommended)", value: "react-swc-ts" }
|
|
452
|
+
],
|
|
453
|
+
initial: 0
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
onCancel: () => {
|
|
457
|
+
logger.error("Template selection cancelled.");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
const variant = variantResponse.template;
|
|
463
|
+
const srcFolderResponse = await prompts(
|
|
464
|
+
{
|
|
465
|
+
type: "confirm",
|
|
466
|
+
name: "useSrc",
|
|
467
|
+
message: "Do you want to use a 'src' folder?",
|
|
468
|
+
initial: true
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
onCancel: () => {
|
|
472
|
+
logger.error("Src folder selection cancelled.");
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
const useSrc = srcFolderResponse.useSrc;
|
|
478
|
+
const tailwindResponse = await prompts(
|
|
479
|
+
{
|
|
480
|
+
type: "confirm",
|
|
481
|
+
name: "useTailwind",
|
|
482
|
+
message: "Do you want to set up Tailwind CSS?",
|
|
483
|
+
initial: true
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
onCancel: () => {
|
|
487
|
+
logger.error("Tailwind CSS setup cancelled.");
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
const useTailwind = tailwindResponse.useTailwind;
|
|
493
|
+
const createCommand = pm.createProjectCommand(projectName, variant);
|
|
494
|
+
logger.info(`Creating project "${projectName}" using ${pm.name}...`);
|
|
495
|
+
try {
|
|
496
|
+
child_process.execSync(createCommand, {
|
|
497
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
498
|
+
input: "n\n"
|
|
499
|
+
});
|
|
500
|
+
} catch (error) {
|
|
501
|
+
}
|
|
502
|
+
logger.info("Installing dependencies...");
|
|
503
|
+
child_process.execSync(pm.installCommand, { stdio: "inherit", cwd: projectName });
|
|
504
|
+
logger.info(
|
|
505
|
+
"Adding next-vite-router and react-router-dom as dependencies..."
|
|
506
|
+
);
|
|
507
|
+
const addDepsCmd = pm.addDepsCommand([
|
|
508
|
+
"next-vite-router",
|
|
509
|
+
"react-router-dom@latest"
|
|
510
|
+
]);
|
|
511
|
+
child_process.execSync(addDepsCmd, { stdio: "inherit", cwd: projectName });
|
|
512
|
+
logger.info("Configuring next-vite-router in the project...");
|
|
513
|
+
startNextViteRouterConfig({
|
|
514
|
+
projectDir: `./${projectName}`,
|
|
515
|
+
src: useSrc,
|
|
516
|
+
tailwindcss: useTailwind
|
|
517
|
+
});
|
|
518
|
+
logger.success(`
|
|
519
|
+
Project "${projectName}" is ready!
|
|
520
|
+
`);
|
|
521
|
+
});
|
|
522
|
+
async function main() {
|
|
523
|
+
const program = new commander.Command();
|
|
524
|
+
program.name("next-vite-router").version("1.0.5").addCommand(init);
|
|
525
|
+
program.parse(process.argv);
|
|
526
|
+
}
|
|
527
|
+
main();
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import i, {
|
|
2
|
-
import { nextViteRouter as
|
|
1
|
+
import i, { Suspense as g, lazy as z } from "react";
|
|
2
|
+
import { nextViteRouter as D } from "./plugin.js";
|
|
3
3
|
function f(e) {
|
|
4
4
|
return /^\([^)]+\)$/.test(e) ? "" : /^\[\.\.\.(.+)\]$/.test(e) ? "*" : /^\[([^\]]+)\]$/.test(e) ? e.replace(/^\[([^\]]+)\]$/, ":$1") : e;
|
|
5
5
|
}
|
|
@@ -7,78 +7,83 @@ function F(e) {
|
|
|
7
7
|
return e.replace("../../app/", "").replace(/\/?(page|layout|not-found)\.tsx$/, "");
|
|
8
8
|
}
|
|
9
9
|
let m = {};
|
|
10
|
-
function
|
|
10
|
+
function k(e) {
|
|
11
11
|
m = { ...m, ...e };
|
|
12
12
|
}
|
|
13
13
|
function C() {
|
|
14
14
|
return m;
|
|
15
15
|
}
|
|
16
16
|
let d = [];
|
|
17
|
-
function
|
|
17
|
+
function S(e, a) {
|
|
18
18
|
d.push({ pattern: e, middleware: a });
|
|
19
19
|
}
|
|
20
20
|
function T() {
|
|
21
21
|
d = [];
|
|
22
22
|
}
|
|
23
|
-
function
|
|
23
|
+
function E(e, a) {
|
|
24
24
|
let r = a;
|
|
25
25
|
for (const n of d)
|
|
26
26
|
n.pattern.test(e) && (r = n.middleware(r));
|
|
27
27
|
return r;
|
|
28
28
|
}
|
|
29
|
-
const x = () => i.createElement("div"),
|
|
30
|
-
function
|
|
29
|
+
const x = () => i.createElement("div"), y = /* @__PURE__ */ new Map();
|
|
30
|
+
function R(e, a, r) {
|
|
31
31
|
const n = C();
|
|
32
32
|
if (n.enableLazyLoading === !1) {
|
|
33
|
-
const u = i.lazy(e), c = i.createElement(
|
|
34
|
-
|
|
33
|
+
const u = i.lazy(e), c = i.createElement(
|
|
34
|
+
g,
|
|
35
|
+
{ fallback: null },
|
|
36
|
+
// No loading fallback when disabled
|
|
37
|
+
i.createElement(u)
|
|
38
|
+
);
|
|
39
|
+
return r ? E(r, c) : c;
|
|
35
40
|
}
|
|
36
|
-
let t =
|
|
37
|
-
t || (t = z(e),
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
{ fallback: i.createElement(
|
|
41
|
+
let t = y.get(a);
|
|
42
|
+
t || (t = z(e), y.set(a, t));
|
|
43
|
+
const l = n.loadingFallback || x, o = i.createElement(
|
|
44
|
+
g,
|
|
45
|
+
{ fallback: i.createElement(l) },
|
|
41
46
|
i.createElement(t)
|
|
42
47
|
);
|
|
43
|
-
return r ?
|
|
48
|
+
return r ? E(r, o) : o;
|
|
44
49
|
}
|
|
45
50
|
function p(e, a, r, n) {
|
|
46
|
-
const t = F(a),
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
u = u ? `${u}/${c}` : c,
|
|
51
|
+
const t = F(a), l = t ? t.split("/") : [];
|
|
52
|
+
let o = e, u = "";
|
|
53
|
+
l.forEach((c, w) => {
|
|
54
|
+
u = u ? `${u}/${c}` : c, o.children.has(c) || o.children.set(c, {
|
|
50
55
|
segment: c,
|
|
51
56
|
fullPath: u,
|
|
52
57
|
children: /* @__PURE__ */ new Map()
|
|
53
|
-
}),
|
|
54
|
-
}),
|
|
58
|
+
}), o = o.children.get(c), w === l.length - 1 && (o[n] = r);
|
|
59
|
+
}), l.length === 0 && (e[n] = r);
|
|
55
60
|
}
|
|
56
|
-
function
|
|
61
|
+
function L(e, a, r) {
|
|
57
62
|
const n = {
|
|
58
63
|
segment: "",
|
|
59
64
|
fullPath: "",
|
|
60
65
|
children: /* @__PURE__ */ new Map()
|
|
61
66
|
};
|
|
62
|
-
return Object.entries(a).forEach(([t,
|
|
63
|
-
p(n, t,
|
|
64
|
-
}), Object.entries(e).forEach(([t,
|
|
65
|
-
p(n, t,
|
|
66
|
-
}), Object.entries(r).forEach(([t,
|
|
67
|
-
p(n, t,
|
|
67
|
+
return Object.entries(a).forEach(([t, l]) => {
|
|
68
|
+
p(n, t, l.default, "layout");
|
|
69
|
+
}), Object.entries(e).forEach(([t, l]) => {
|
|
70
|
+
p(n, t, l, "page");
|
|
71
|
+
}), Object.entries(r).forEach(([t, l]) => {
|
|
72
|
+
p(n, t, l, "notFound");
|
|
68
73
|
}), n;
|
|
69
74
|
}
|
|
70
75
|
function h(e, a, r = !1) {
|
|
71
|
-
const n =
|
|
76
|
+
const n = R(
|
|
72
77
|
async () => ({ default: (await e.page()).default }),
|
|
73
78
|
`page:${e.fullPath || a}`,
|
|
74
79
|
a
|
|
75
80
|
);
|
|
76
81
|
return r ? { index: !0, element: n } : { path: f(e.segment), element: n };
|
|
77
82
|
}
|
|
78
|
-
function
|
|
83
|
+
function $(e) {
|
|
79
84
|
return {
|
|
80
85
|
path: "*",
|
|
81
|
-
element:
|
|
86
|
+
element: R(
|
|
82
87
|
async () => ({ default: (await e.notFound()).default }),
|
|
83
88
|
`notfound:${e.fullPath}`
|
|
84
89
|
)
|
|
@@ -87,15 +92,15 @@ function y(e) {
|
|
|
87
92
|
function s(e, a) {
|
|
88
93
|
const r = [];
|
|
89
94
|
return e.children.forEach((n, t) => {
|
|
90
|
-
const
|
|
91
|
-
r.push(...
|
|
95
|
+
const l = f(t), o = l ? a ? `${a}/${l}` : `/${l}` : a;
|
|
96
|
+
r.push(...b(n, !1, o));
|
|
92
97
|
}), r;
|
|
93
98
|
}
|
|
94
|
-
function
|
|
99
|
+
function b(e, a = !1, r = "") {
|
|
95
100
|
const n = [];
|
|
96
101
|
if (a && e.layout) {
|
|
97
102
|
const t = [];
|
|
98
|
-
return e.page && t.push(h(e, "/", !0)), t.push(...s(e, "")), e.notFound && t.push(
|
|
103
|
+
return e.page && t.push(h(e, "/", !0)), t.push(...s(e, "")), e.notFound && t.push($(e)), n.push({
|
|
99
104
|
path: "/",
|
|
100
105
|
element: i.createElement(e.layout),
|
|
101
106
|
children: t
|
|
@@ -105,41 +110,41 @@ function R(e, a = !1, r = "") {
|
|
|
105
110
|
const t = f(e.segment);
|
|
106
111
|
if (!t)
|
|
107
112
|
return s(e, r);
|
|
108
|
-
const
|
|
109
|
-
return e.page &&
|
|
113
|
+
const l = r + "/" + t, o = [];
|
|
114
|
+
return e.page && o.push(h(e, l, !0)), o.push(...s(e, l)), e.notFound && o.push($(e)), n.push({
|
|
110
115
|
path: t,
|
|
111
116
|
element: i.createElement(e.layout),
|
|
112
|
-
children:
|
|
117
|
+
children: o.length > 0 ? o : void 0
|
|
113
118
|
}), n;
|
|
114
119
|
}
|
|
115
120
|
if (e.page) {
|
|
116
121
|
const t = f(e.segment);
|
|
117
122
|
if (!t)
|
|
118
123
|
return s(e, r);
|
|
119
|
-
const
|
|
124
|
+
const l = r + "/" + t, o = s(e, l), u = h(e, l, !1);
|
|
120
125
|
return n.push({
|
|
121
126
|
path: u.path,
|
|
122
127
|
element: u.element,
|
|
123
|
-
children:
|
|
128
|
+
children: o.length > 0 ? o : void 0
|
|
124
129
|
}), n;
|
|
125
130
|
}
|
|
126
131
|
return s(e, r);
|
|
127
132
|
}
|
|
128
133
|
function j(e, a, r) {
|
|
129
134
|
return () => {
|
|
130
|
-
const n =
|
|
131
|
-
return
|
|
135
|
+
const n = L(e, a, r);
|
|
136
|
+
return b(n, !0);
|
|
132
137
|
};
|
|
133
138
|
}
|
|
134
139
|
export {
|
|
135
|
-
|
|
140
|
+
L as buildRouteTree,
|
|
136
141
|
T as clearMiddlewares,
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
k as configureRouter,
|
|
143
|
+
R as createLazyElement,
|
|
139
144
|
j as createRouteGenerator,
|
|
140
145
|
F as extractRoutePath,
|
|
141
|
-
|
|
146
|
+
D as nextViteRouter,
|
|
142
147
|
f as normalizeSegment,
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
S as registerMiddleware,
|
|
149
|
+
b as treeToRoutes
|
|
145
150
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-vite-router",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Next.js-like file-based routing for React + Vite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"next-vite-router": "./dist/cli.cjs"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"import": "./dist/index.js",
|
|
@@ -44,16 +47,23 @@
|
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"@types/node": "^24.10.1",
|
|
50
|
+
"@types/prompts": "^2.4.9",
|
|
47
51
|
"@types/react": "^18.0.0",
|
|
48
52
|
"@types/react-dom": "^18.0.0",
|
|
49
53
|
"typescript": "^5.0.0",
|
|
50
54
|
"vite": "^5.0.0",
|
|
51
55
|
"vite-plugin-dts": "^3.0.0"
|
|
52
56
|
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"commander": "^14.0.2",
|
|
59
|
+
"kleur": "^4.1.5",
|
|
60
|
+
"prompts": "^2.4.2"
|
|
61
|
+
},
|
|
53
62
|
"scripts": {
|
|
54
63
|
"dev": "vite build --watch",
|
|
55
|
-
"build": "vite build",
|
|
64
|
+
"build": "vite build && vite build -c vite.config.cli.ts",
|
|
56
65
|
"test:local": "node test/test-package.js",
|
|
57
|
-
"preview": "pnpm pack --dry-run"
|
|
66
|
+
"preview": "pnpm pack --dry-run",
|
|
67
|
+
"cli": "cd test && node ../dist/cli.cjs init"
|
|
58
68
|
}
|
|
59
69
|
}
|