@metricinsights/pp-dev 0.10.1 ā 0.11.0-beta.4
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/CHANGELOG.md +34 -2
- package/README.md +207 -4
- package/dist/CHANGELOG.md +176 -0
- package/dist/README.md +449 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/index-DGgPiX1I.js +2 -0
- package/dist/cjs/index-DGgPiX1I.js.map +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/package.json +11 -37
- package/dist/cjs/plugin-CqSS_2nh.js +2 -0
- package/dist/cjs/plugin-CqSS_2nh.js.map +1 -0
- package/dist/cjs/plugin.js +1 -1
- package/dist/client/client.js +7 -0
- package/dist/client/client.js.map +1 -1
- package/dist/esm/cli.js +1 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index-DMOFygHY.js +2 -0
- package/dist/esm/index-DMOFygHY.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/package.json +11 -37
- package/dist/esm/plugin-qc60R-dL.js +2 -0
- package/dist/esm/plugin-qc60R-dL.js.map +1 -0
- package/dist/esm/plugin.js +1 -1
- package/dist/types/index.d.ts +259 -4
- package/package.json +21 -6
- package/dist/cjs/index-DRre2UkM.js +0 -2
- package/dist/cjs/index-DRre2UkM.js.map +0 -1
- package/dist/cjs/plugin-Dx9CDaej.js +0 -2
- package/dist/cjs/plugin-Dx9CDaej.js.map +0 -1
- package/dist/esm/index-kmS_-y6Z.js +0 -2
- package/dist/esm/index-kmS_-y6Z.js.map +0 -1
- package/dist/esm/plugin-H4ccpQoX.js +0 -2
- package/dist/esm/plugin-H4ccpQoX.js.map +0 -1
package/dist/README.md
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# pp-dev
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/@metricinsights/pp-dev"><img alt="npm" src="https://img.shields.io/npm/v/%40metricinsights%2Fpp-dev?logo=npm&label=npm%20package"></a>
|
|
5
|
+
<a href="https://github.com/mi-examples/pp-dev-js/actions/workflows/publish.yml"><img alt="GitHub Workflow Status (with event)" src="https://img.shields.io/github/actions/workflow/status/mi-examples/pp-dev-js/publish.yml?logo=github"></a>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
The PP Dev Helper is a development framework and build tool for Metric Insights' Portal Pages, designed to make the
|
|
9
|
+
lives of PP developers easier:
|
|
10
|
+
|
|
11
|
+
- Build and test Portal Pages locally
|
|
12
|
+
- Proxy API requests to a Metric Insights server
|
|
13
|
+
- Hot module replacement for faster development
|
|
14
|
+
- Image optimization and asset management
|
|
15
|
+
- Template variable transformation
|
|
16
|
+
- Code synchronization with Metric Insights instances
|
|
17
|
+
|
|
18
|
+
pp-dev is based on [Vite](https://vitejs.dev/).
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @metricinsights/pp-dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Peer Dependencies
|
|
27
|
+
|
|
28
|
+
This package requires Next.js as a peer dependency for certain functionality:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install next@^15
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Note**: pp-dev requires Next.js version 15 or higher (but less than 17) to be installed in your project. This is a peer dependency, meaning it won't be automatically installed with pp-dev.
|
|
35
|
+
|
|
36
|
+
## Package Structure
|
|
37
|
+
|
|
38
|
+
The pp-dev package provides multiple entry points for different use cases:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// Main package (includes everything)
|
|
42
|
+
import ppDev from '@metricinsights/pp-dev';
|
|
43
|
+
|
|
44
|
+
// Plugin only (for Vite integration)
|
|
45
|
+
import { vitePPDev } from '@metricinsights/pp-dev/plugin';
|
|
46
|
+
|
|
47
|
+
// Helpers only (for utility functions)
|
|
48
|
+
import { helpers } from '@metricinsights/pp-dev/helpers';
|
|
49
|
+
|
|
50
|
+
// Client assets (for development UI)
|
|
51
|
+
import '@metricinsights/pp-dev/client/css/client.css';
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Available Exports**:
|
|
55
|
+
- **Main**: Complete pp-dev functionality with CLI and plugins
|
|
56
|
+
- **Plugin**: Vite plugin for integration with build tools
|
|
57
|
+
- **Helpers**: Utility functions for authentication and configuration
|
|
58
|
+
- **Client**: Development UI assets and styles
|
|
59
|
+
|
|
60
|
+
## š Performance & Build System
|
|
61
|
+
|
|
62
|
+
The pp-dev package includes optimized startup performance and build system with multiple strategies:
|
|
63
|
+
|
|
64
|
+
### Quick Start
|
|
65
|
+
```bash
|
|
66
|
+
# Standard build (parallel)
|
|
67
|
+
npm run build
|
|
68
|
+
|
|
69
|
+
# Fast development build
|
|
70
|
+
npm run build:fast
|
|
71
|
+
|
|
72
|
+
# Watch mode for development
|
|
73
|
+
npm run build:watch
|
|
74
|
+
|
|
75
|
+
# Bundle analysis
|
|
76
|
+
npm run build:analyze
|
|
77
|
+
|
|
78
|
+
# Performance profiling
|
|
79
|
+
npm run startup:profile
|
|
80
|
+
|
|
81
|
+
# Startup optimization
|
|
82
|
+
npm run startup:optimize
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Performance Features
|
|
86
|
+
- **40-50% faster startup** with intelligent caching
|
|
87
|
+
- **60-70% faster subsequent starts** with connection pooling
|
|
88
|
+
- **Lazy loading** of heavy modules (jsdom, esbuild)
|
|
89
|
+
- **API response caching** with configurable TTL
|
|
90
|
+
- **HTTP connection pooling** for reduced overhead
|
|
91
|
+
- **Startup profiling** with detailed performance analysis
|
|
92
|
+
- **Intelligent dependency optimization** based on profiling data
|
|
93
|
+
|
|
94
|
+
### Build Features
|
|
95
|
+
- **Parallel builds** for 40-60% faster build times
|
|
96
|
+
- **Enhanced tree-shaking** for smaller bundles
|
|
97
|
+
- **Multiple output formats** (ESM, CJS, Types)
|
|
98
|
+
- **Bundle analysis** with visualizer support
|
|
99
|
+
- **ESBuild integration** for faster TypeScript compilation
|
|
100
|
+
- **Build optimization scripts** for performance tuning
|
|
101
|
+
|
|
102
|
+
### Startup Optimization
|
|
103
|
+
|
|
104
|
+
The new startup optimization system in v0.11.0 provides:
|
|
105
|
+
|
|
106
|
+
- **Performance Monitoring**: Real-time startup time tracking and analysis
|
|
107
|
+
- **Cache Optimization**: Intelligent cache management for config and API responses
|
|
108
|
+
- **Dependency Analysis**: Identification of performance bottlenecks
|
|
109
|
+
- **Optimization Suggestions**: Automated recommendations for performance improvements
|
|
110
|
+
|
|
111
|
+
Run the startup optimizer to analyze and improve your development environment:
|
|
112
|
+
```bash
|
|
113
|
+
npm run startup:optimize
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
š See [BUILD_IMPROVEMENTS.md](./BUILD_IMPROVEMENTS.md) for build details.
|
|
117
|
+
š See [STARTUP_PERFORMANCE.md](./STARTUP_PERFORMANCE.md) for performance details.
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
### Configuration File
|
|
122
|
+
|
|
123
|
+
Create a configuration file named `pp-dev.config` with one of these extensions:
|
|
124
|
+
- `.js` or `.cjs` (for CommonJS)
|
|
125
|
+
- `.ts` (for TypeScript)
|
|
126
|
+
- `.json`
|
|
127
|
+
|
|
128
|
+
Alternatively, you can define configuration in your `package.json` using the `pp-dev` key.
|
|
129
|
+
|
|
130
|
+
### Configuration Examples
|
|
131
|
+
|
|
132
|
+
#### JavaScript (CommonJS)
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// pp-dev.config.js
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @type {import('@metricinsights/pp-dev').PPDevConfig}
|
|
139
|
+
*/
|
|
140
|
+
module.exports = {
|
|
141
|
+
backendBaseURL: 'https://mi.company.com',
|
|
142
|
+
appId: 1,
|
|
143
|
+
v7Features: true,
|
|
144
|
+
miHudLess: true,
|
|
145
|
+
integrateMiTopBar: true,
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### TypeScript
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// pp-dev.config.ts
|
|
153
|
+
|
|
154
|
+
import { PPDevConfig } from '@metricinsights/pp-dev';
|
|
155
|
+
|
|
156
|
+
const config: PPDevConfig = {
|
|
157
|
+
backendBaseURL: 'https://mi.company.com',
|
|
158
|
+
appId: 1,
|
|
159
|
+
v7Features: true,
|
|
160
|
+
miHudLess: true,
|
|
161
|
+
integrateMiTopBar: true,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export default config;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### JSON
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
// pp-dev.config.json
|
|
171
|
+
{
|
|
172
|
+
"backendBaseURL": "https://mi.company.com",
|
|
173
|
+
"appId": 1,
|
|
174
|
+
"v7Features": true,
|
|
175
|
+
"miHudLess": true,
|
|
176
|
+
"integrateMiTopBar": true
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### package.json
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"name": "my-portal-page",
|
|
185
|
+
"version": "1.0.0",
|
|
186
|
+
"pp-dev": {
|
|
187
|
+
"backendBaseURL": "https://mi.company.com",
|
|
188
|
+
"appId": 1,
|
|
189
|
+
"v7Features": true,
|
|
190
|
+
"miHudLess": true,
|
|
191
|
+
"integrateMiTopBar": true
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Configuration Options
|
|
197
|
+
|
|
198
|
+
> **Version Compatibility**: This documentation covers pp-dev v0.11.0+. Some options may not be available in older versions. Check the [CHANGELOG](./CHANGELOG.md) for version-specific information.
|
|
199
|
+
|
|
200
|
+
### Required Options
|
|
201
|
+
|
|
202
|
+
| Option | Type | Description |
|
|
203
|
+
|--------|------|-------------|
|
|
204
|
+
| `backendBaseURL` | string | URL of the Metric Insights instance for API proxying |
|
|
205
|
+
| `portalPageId` | number | ID of the Portal Page for variable values (deprecated, use `appId` instead) |
|
|
206
|
+
| `appId` | number | ID of the Portal Page for variable values (synonym for `portalPageId`) |
|
|
207
|
+
|
|
208
|
+
### Optional Options
|
|
209
|
+
|
|
210
|
+
| Option | Type | Default | Description |
|
|
211
|
+
|--------|------|---------|-------------|
|
|
212
|
+
| `miHudLess` | boolean | `false` | Disables Metric Insights navigation bar in development |
|
|
213
|
+
| `integrateMiTopBar` | boolean | `false` | Integrates MI Top Bar and script into the App build (requires `miHudLess: true`) |
|
|
214
|
+
| `templateLess` | boolean | `false` | Disables template variable transformation |
|
|
215
|
+
| `enableProxyCache` | boolean | `true` | Enables caching of proxied requests |
|
|
216
|
+
| `proxyCacheTTL` | number | `600000` | Cache TTL in milliseconds (10 minutes) |
|
|
217
|
+
| `disableSSLValidation` | boolean | `false` | Disables SSL certificate validation for proxy requests |
|
|
218
|
+
| `imageOptimizer` | boolean \| object | `true` | Controls image optimization. See [vite-plugin-image-optimizer](https://www.npmjs.com/package/vite-plugin-image-optimizer#plugin-options) for object options |
|
|
219
|
+
| `outDir` | string | `dist` | Output directory for builds |
|
|
220
|
+
| `distZip` | boolean \| object | `true` | Controls build output zipping. Object options: `{ outDir?: string, outFileName?: string }` |
|
|
221
|
+
| `syncBackupsDir` | string | `backups` | Directory for asset backups from MI server |
|
|
222
|
+
| `v7Features` | boolean | `false` | Enables Metric Insights v7 features |
|
|
223
|
+
| `personalAccessToken` | string | `process.env.MI_ACCESS_TOKEN` | Personal Access Token for the MI instance |
|
|
224
|
+
|
|
225
|
+
### integrateMiTopBar Details
|
|
226
|
+
|
|
227
|
+
The `integrateMiTopBar` option allows you to integrate the Metric Insights Top Bar and scripts directly into your application build. This is useful when you want to:
|
|
228
|
+
|
|
229
|
+
1. **Customize the Top Bar**: Modify the appearance and behavior of the MI navigation
|
|
230
|
+
2. **Bundle Integration**: Include MI scripts in your build instead of loading them dynamically
|
|
231
|
+
3. **Offline Development**: Work with MI features even when disconnected from the server
|
|
232
|
+
|
|
233
|
+
**Important**: This option can only be enabled when `miHudLess` is set to `true`.
|
|
234
|
+
|
|
235
|
+
Example configuration:
|
|
236
|
+
```javascript
|
|
237
|
+
// pp-dev.config.js
|
|
238
|
+
module.exports = {
|
|
239
|
+
backendBaseURL: 'https://mi.company.com',
|
|
240
|
+
appId: 1,
|
|
241
|
+
miHudLess: true, // Required: Disable dynamic MI scripts
|
|
242
|
+
integrateMiTopBar: true, // Enable: Integrate Top Bar into build
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### v7Features Details
|
|
247
|
+
|
|
248
|
+
When enabled (`true`), this option:
|
|
249
|
+
1. Changes development path from `/pt/<portal-page-name>` to `/pl/<portal-page-name>`
|
|
250
|
+
2. Updates Code Sync feature to use v7.1.0+ URLs
|
|
251
|
+
|
|
252
|
+
### Personal Access Token
|
|
253
|
+
|
|
254
|
+
The `personalAccessToken` option allows you to authenticate with the Metric Insights instance. You can set it in your configuration or use the `MI_ACCESS_TOKEN` environment variable.
|
|
255
|
+
|
|
256
|
+
Example with authentication and Top Bar integration:
|
|
257
|
+
```javascript
|
|
258
|
+
// pp-dev.config.js
|
|
259
|
+
module.exports = {
|
|
260
|
+
backendBaseURL: 'https://mi.company.com',
|
|
261
|
+
appId: 1,
|
|
262
|
+
personalAccessToken: process.env.MI_ACCESS_TOKEN,
|
|
263
|
+
miHudLess: true,
|
|
264
|
+
integrateMiTopBar: true,
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Environment Variable**: Set `MI_ACCESS_TOKEN` in your `.env` file:
|
|
269
|
+
```bash
|
|
270
|
+
MI_ACCESS_TOKEN=your_token_here
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Enhanced Authentication (v0.11.0+)
|
|
274
|
+
|
|
275
|
+
The new authentication system in v0.11.0 provides:
|
|
276
|
+
|
|
277
|
+
- **Automatic Environment Loading**: Automatically loads `MI_*` environment variables from `.env` files
|
|
278
|
+
- **Token Validation**: Enhanced token validation and error handling
|
|
279
|
+
- **Secure Headers**: Automatic header management for authenticated requests
|
|
280
|
+
- **Connection Pooling**: Optimized HTTP connections for better performance
|
|
281
|
+
|
|
282
|
+
**Supported Environment Variables**:
|
|
283
|
+
- `MI_ACCESS_TOKEN`: Personal access token for authentication
|
|
284
|
+
- `MI_BACKEND_URL`: Alternative to `backendBaseURL` in config
|
|
285
|
+
- `MI_APP_ID`: Alternative to `appId` in config
|
|
286
|
+
|
|
287
|
+
**Automatic Loading**: pp-dev automatically detects and loads these variables from your project's `.env` file:
|
|
288
|
+
```bash
|
|
289
|
+
# .env
|
|
290
|
+
MI_ACCESS_TOKEN=your_personal_access_token
|
|
291
|
+
MI_BACKEND_URL=https://mi.company.com
|
|
292
|
+
MI_APP_ID=123
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## CLI Commands
|
|
296
|
+
|
|
297
|
+
### Global Options
|
|
298
|
+
|
|
299
|
+
| Option | Description |
|
|
300
|
+
|--------|-------------|
|
|
301
|
+
| `-c, --config <file>` | Path to configuration file (default: `pp-dev.config.js`) |
|
|
302
|
+
| `--base <path>` | Public base path (default: `/`) |
|
|
303
|
+
| `-l, --logLevel <level>` | Log level: `trace`, `debug`, `info`, `warn`, `error`, `silent` (default: `info`) |
|
|
304
|
+
| `--clearScreen` | Clear screen before logging |
|
|
305
|
+
| `--mode <mode>` | Environment mode: `development`, `production`, `test` (default: `development`) |
|
|
306
|
+
|
|
307
|
+
### Development Server
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
pp-dev [root] [options]
|
|
311
|
+
# Aliases: pp-dev dev, pp-dev serve
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
| Option | Default | Description |
|
|
315
|
+
|--------|---------|-------------|
|
|
316
|
+
| `[root]` | `.` | Root directory of the application |
|
|
317
|
+
| `--host <host>` | `localhost` | Server hostname |
|
|
318
|
+
| `--port <port>` | `3000` | Server port |
|
|
319
|
+
| `--open [path]` | - | Open browser on server start |
|
|
320
|
+
| `--strictPort` | - | Exit if port is already in use |
|
|
321
|
+
|
|
322
|
+
**Development Shortcuts**:
|
|
323
|
+
- `p` - Start/stop performance profiler (v0.11.0+)
|
|
324
|
+
- `l` - Proxy re-login (refresh authentication)
|
|
325
|
+
- `r` - Restart dev server
|
|
326
|
+
- `u` - Show server URLs
|
|
327
|
+
- `q` - Quit dev server
|
|
328
|
+
|
|
329
|
+
**Performance Profiling**: Use the `p` shortcut to start/stop the Node.js profiler for detailed performance analysis during development.
|
|
330
|
+
|
|
331
|
+
### Next.js Development
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
pp-dev next [options]
|
|
335
|
+
# Aliases: pp-dev next-server, pp-dev next-dev
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
| Option | Default | Description |
|
|
339
|
+
|--------|---------|-------------|
|
|
340
|
+
| `[root]` | `.` | Root directory of the application |
|
|
341
|
+
| `--port <port>` | `3000` | Server port |
|
|
342
|
+
| `--host <host>` | `localhost` | Server hostname |
|
|
343
|
+
|
|
344
|
+
### Build
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
pp-dev build [options]
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
| Option | Default | Description |
|
|
351
|
+
|--------|---------|-------------|
|
|
352
|
+
| `[root]` | `.` | Root directory of the application |
|
|
353
|
+
| `--target <target>` | `modules` | Transpile target |
|
|
354
|
+
| `--outDir <dir>` | `dist` | Output directory |
|
|
355
|
+
| `--assetsDir <dir>` | `assets` | Assets directory under outDir |
|
|
356
|
+
| `--changelog [file]` | `true` | Create changelog file |
|
|
357
|
+
|
|
358
|
+
### Changelog Generation
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
pp-dev changelog [oldAssetPath] [newAssetPath] [options]
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
| Option | Default | Description |
|
|
365
|
+
|--------|---------|-------------|
|
|
366
|
+
| `[oldAssetPath]` | - | Path to previous assets |
|
|
367
|
+
| `[newAssetPath]` | - | Path to current assets |
|
|
368
|
+
| `--oldAssetsPath <path>` | - | Path to previous assets |
|
|
369
|
+
| `--newAssetsPath <path>` | - | Path to current assets |
|
|
370
|
+
| `--destination <path>` | `.` | Changelog output directory |
|
|
371
|
+
| `--filename <name>` | `CHANGELOG.html` | Changelog filename |
|
|
372
|
+
|
|
373
|
+
### Icon Font Generation
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
pp-dev generate-icon-font [source] [destination] [options]
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
| Option | Default | Description |
|
|
380
|
+
|--------|---------|-------------|
|
|
381
|
+
| `[source]` | - | Source directory with SVG icons |
|
|
382
|
+
| `[destination]` | - | Output directory |
|
|
383
|
+
| `--source <path>` | - | Source directory with SVG icons |
|
|
384
|
+
| `--destination <path>` | - | Output directory |
|
|
385
|
+
| `--fontName <name>` | `icon-font` | Font name |
|
|
386
|
+
|
|
387
|
+
## Next.js Integration
|
|
388
|
+
|
|
389
|
+
1. Add pp-dev configuration to your project root
|
|
390
|
+
2. Update `package.json` scripts:
|
|
391
|
+
```json
|
|
392
|
+
{
|
|
393
|
+
"scripts": {
|
|
394
|
+
"dev": "pp-dev next"
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
3. Wrap your Next.js config:
|
|
399
|
+
```javascript
|
|
400
|
+
// next.config.js
|
|
401
|
+
const { withPPDev } = require('@metricinsights/pp-dev');
|
|
402
|
+
|
|
403
|
+
module.exports = withPPDev({
|
|
404
|
+
// your Next.js config
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Vite Configuration
|
|
409
|
+
|
|
410
|
+
For custom build configuration, create a `vite.config` file. See [Vite Configuration](https://vitejs.dev/config/) for details.
|
|
411
|
+
|
|
412
|
+
## Troubleshooting
|
|
413
|
+
|
|
414
|
+
### Common Issues
|
|
415
|
+
|
|
416
|
+
#### Next.js Peer Dependency Error
|
|
417
|
+
|
|
418
|
+
If you encounter an error like "Next.js is required but not available":
|
|
419
|
+
|
|
420
|
+
1. **Install Next.js in your project:**
|
|
421
|
+
```bash
|
|
422
|
+
npm install next@^15
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
2. **Verify the installation:**
|
|
426
|
+
```bash
|
|
427
|
+
npm list next
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
3. **Check your package.json:**
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"dependencies": {
|
|
434
|
+
"next": "^15.0.0"
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### Version Compatibility
|
|
440
|
+
|
|
441
|
+
- **pp-dev** requires Next.js version 15 or higher (but less than 17)
|
|
442
|
+
- **Node.js** version 20 or higher is required
|
|
443
|
+
- **TypeScript** version 4.2 or higher is supported
|
|
444
|
+
|
|
445
|
+
### Getting Help
|
|
446
|
+
|
|
447
|
+
- Check the [GitHub Issues](https://github.com/mi-examples/pp-dev-js/issues) for known problems
|
|
448
|
+
- Review the [CHANGELOG.md](./CHANGELOG.md) for recent changes
|
|
449
|
+
- Ensure all peer dependencies are properly installed
|
package/dist/cjs/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("path"),t=require("fs"),o=require("node:perf_hooks"),n=require("cac"),i=require("vite"),r=require("./index-DRre2UkM.js"),s=require("./plugin-Dx9CDaej.js"),a=require("url"),l=require("express"),c=require("winston"),d=require("dir-compare"),p=require("diff-match-patch"),f=require("isbinaryfile"),g=require("os"),h=require("crypto"),u=require("extract-zip"),m=require("svgtofont"),b=require("node:process");function v(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(o){if("default"!==o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}})),t.default=e,Object.freeze(t)}require("ejs"),require("esbuild"),require("http-proxy-middleware"),require("picocolors"),require("axios"),require("jsdom"),require("https"),require("memory-cache"),require("process"),require("child_process"),require("console"),require("zlib");var w=v(e),y=v(t),L=v(l),P=v(c),T=v(d),x=v(g),F=v(h),$=v(b);function S(e){return null!==e||void 0!==e}const C=[{key:"r",description:"restart the server",async action(e){await e.restart()}},{key:"u",description:"show server url",action(e){e.config.logger.info(""),e.printUrls()}},{key:"o",description:"open in browser",action(e){e.openBrowser()}},{key:"c",description:"clear console",action(e){e.config.logger.clearScreen("error")}},{key:"q",description:"quit",async action(e){await e.close().finally((()=>process.exit()))}},{key:"C",description:"clear proxy cache",action(e){e.cache&&(e.cache.clear(),e.config.logger.info("Proxy cache cleared"))}}];const k=/(.+)(-[a-f0-9]{6,20})(\.[a-z0-9]+)$/i;class A{oldAssetsPath;newAssetsPath;destinationPath;changelogFilename;changelogTemplate='<!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8" />\n <title>Changelog Diff</title>\n <style>\n tr,\n td {\n padding: 0;\n }\n .diff-file {\n margin-top: 20px;\n border: 1px solid #e1e4e8;\n border-radius: 6px;\n }\n .diff-file-title {\n padding: 10px 20px;\n background-color: #f6f8fa;\n border-bottom: 1px solid #e1e4e8;\n border-radius: 6px 6px 0 0;\n font-weight: bold;\n }\n .diff-file-title .renamed {\n font-weight: normal;\n }\n .diff-file-title .renamed .from {\n color: #cb2431;\n }\n .diff-file-title .renamed .to {\n color: #22863a;\n }\n .diff-file-title .added {\n color: #22863a;\n }\n .diff-file-title .deleted {\n color: #cb2431;\n }\n .diff-file-content {\n }\n .diff-table {\n tab-size: 8;\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n }\n .blob-num {\n position: relative;\n color: #1f2328;\n width: 1%;\n min-width: 50px;\n padding: 0 10px;\n font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;\n font-size: 12px;\n line-height: 20px;\n text-align: right;\n white-space: nowrap;\n vertical-align: top;\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n }\n .blob-num.addition {\n background-color: #ccffd8;\n border-color: #1f883e;\n }\n .blob-num.deletion {\n background-color: #ffd7d5;\n border-color: #cf222e;\n }\n .blob-num::before {\n content: attr(data-line-number);\n }\n .blob-code {\n position: relative;\n padding: 0 10px 0 22px;\n vertical-align: top;\n color: #1f2329;\n }\n .blob-code.code-addition {\n background-color: #e6ffec;\n }\n .blob-code.code-deletion {\n background-color: #ffebe9;\n }\n .blob-code.skip,\n .blob-code.message {\n text-align: center;\n }\n .blob-code.skip .blob-code-inner,\n .blob-code.message .blob-code-inner {\n font-weight: bold;\n color: #6a737d;\n padding: 10px 0;\n }\n .blob-code-inner {\n display: table-cell;\n overflow: visible;\n font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;\n font-size: 12px;\n word-wrap: anywhere;\n white-space: pre-wrap;\n }\n .blob-code-inner::before {\n content: attr(data-code-prefix);\n position: absolute;\n top: 1px;\n left: 8px;\n padding-right: 8px;\n }\n </style>\n </head>\n <body>\n <h1>Changelog Diff</h1>\n\n %FILES%\n </body>\n </html>';diffFileTemplateHandler;diffLineTemplateHandler;contextLines=3;logger;constructor(e){const{oldAssetsPath:t,newAssetsPath:o,destinationPath:n,changelogTemplate:i,diffFileTemplateHandler:r,diffLineTemplateHandler:a,changelogFilename:l,contextLines:c}=e;if(this.logger=s.createLogger(),!t||!o||!n)throw new Error("Previous assets path, current assets path and destination path are required");if(t===o)throw new Error("Previous and current assets paths must be different");if(!this.isExists(t))throw new Error(`Previous assets path ${t} does not exist`);if(!this.isExists(o))throw new Error(`Current assets path ${o} does not exist`);if(this.isZipFile(t)){const e=w.resolve(x.tmpdir(),F.createHash("md5").update(t).digest("hex"));this.oldAssetsPath=this.unzipFile(t,e).then((()=>this.normalizeAssetFolderPath(e))).then((e=>{if(this.isEmptyFolder(e))throw new Error(`Previous assets path ${e} is empty`);return e}))}else{if(!this.isFolder(t))throw new Error(`Invalid previous assets path ${t}. It must be a folder or a zip file`);{const e=this.normalizeAssetFolderPath(t);if(this.isEmptyFolder(e))throw new Error(`Previous assets path ${e} is empty`);this.oldAssetsPath=Promise.resolve(e)}}if(this.isZipFile(o)){const e=w.resolve(x.tmpdir(),F.createHash("md5").update(o).digest("hex"));this.newAssetsPath=this.unzipFile(o,e).then((()=>this.normalizeAssetFolderPath(e))).then((e=>{if(this.isEmptyFolder(e))throw new Error(`Current assets path ${e} is empty`);return e}))}else{if(!this.isFolder(o))throw new Error(`Invalid current assets path ${o}. It must be a folder or a zip file`);{const e=this.normalizeAssetFolderPath(o);if(this.isEmptyFolder(e))throw new Error(`Current assets path ${o} is empty`);this.newAssetsPath=Promise.resolve(e)}}this.destinationPath=n,this.mkdirpSync(this.destinationPath),this.changelogFilename=l||"CHANGELOG.html",i&&(this.templateIsValid(i)?this.changelogTemplate=i:this.logger.warn(s.colors.yellow("Invalid changelog template, using default"))),"function"==typeof r&&(this.diffFileTemplateHandler=r),"function"==typeof a&&(this.diffLineTemplateHandler=a),c&&(this.contextLines=c)}templateIsValid(e){return e.includes("%FILES%")}isExists(e){return y.existsSync(e)}isZipFile(e){return e.endsWith(".zip")}isFolder(e){return y.lstatSync(e).isDirectory()}isEmptyFolder(e){return 0===y.readdirSync(e,{withFileTypes:!0}).length}mkdirpSync(e){y.existsSync(e)||y.mkdirSync(e,{recursive:!0})}async unzipFile(e,t){return y.rmSync(t,{force:!0,recursive:!0}),u(e,{dir:t})}normalizeAssetFolderPath(e){const t=y.readdirSync(e);return 1===t.length&&y.lstatSync(w.join(e,t[0])).isDirectory()?this.normalizeAssetFolderPath(w.join(e,t[0])):e}pathToPosix(e){return e.replace(/\\/g,"/")}diffTableTemplate(e){return`<table class="diff-table">\n <tbody>\n ${e}\n </tbody>\n </table>`}getDiffTableHTML(e){const t=e.filter(((e,t,o)=>0!==e.lineType||(t>=0&&t<this.contextLines||t>o.length-(this.contextLines+1)&&t<=o.length-1||o.slice(t-this.contextLines,t+this.contextLines+1).some((e=>0!==e.lineType))))).map(((e,t,o)=>{if(t>0){const n=o[t-1];if(e.lineNumber-n.lineNumber>1)return[{lineNumber:-1,lineContent:"",lineType:0},e]}return[e]})).flat().map((e=>this.diffLineHTML(e))).join("");return this.diffTableTemplate(t)}diffLineMessageTemplate(e){return`<tr>\n <td class="blob-num"></td>\n <td class="blob-num"></td>\n <td class="blob-code message">\n <span class="blob-code-inner">${e}</span>\n </td>\n </tr>`}diffLineSkipTemplate(){return'<tr>\n <td class="blob-num"></td>\n <td class="blob-num"></td>\n <td class="blob-code skip">\n <span class="blob-code-inner">Skip</span>\n </td>\n </tr>'}diffLineTemplate(e){const{lineNumber:t,lineContent:o,lineType:n}=e,i=1===n?"addition":-1===n?"deletion":"";return`<tr>\n <td\n class="blob-num ${i}${"addition"===i?" empty":""}"\n ${"addition"!==i?` data-line-number="${t}"`:""}\n ></td>\n <td\n class="blob-num ${i}${"deletion"===i?" empty":""}"\n ${"deletion"!==i?` data-line-number="${t}"`:""}\n ></td>\n <td class="blob-code ${1===n?"code-addition":-1===n?"code-deletion":""}">\n <span class="blob-code-inner" data-code-prefix="${1===n?"+":-1===n?"-":" "}">${r=o??"",r.replace(/[\u00A0-\u9999<>&]/g,(e=>"&#"+e.charCodeAt(0)+";"))}</span>\n </td>\n </tr>`;var r}diffLineHTML(e){if(this.diffLineTemplateHandler)return this.diffLineTemplateHandler(e);const{lineNumber:t}=e;return-1===t?this.diffLineSkipTemplate():this.diffLineTemplate(e)}diffFileTemplate(e,t){return this.diffFileTemplateHandler?this.diffFileTemplateHandler(e,t):`<div class="diff-file">\n <div class="diff-file-title">${e}</div>\n <div class="diff-file-content">${t}</div>\n </div>`}async generateAssetFoldersDiff(){this.logger.info(s.colors.blue(`Comparing asset folders ${await this.oldAssetsPath} and ${await this.newAssetsPath}`));const e=await T.compare(await this.oldAssetsPath,await this.newAssetsPath,{compareContent:!0,skipSymlinks:!0,compareSize:!0,compareDate:!1,compareNameHandler:(e,t)=>(k.test(e)&&(e=e.replace(k,"$1$3")),k.test(t)&&(t=t.replace(k,"$1$3")),0===e.localeCompare(t)?0:e.localeCompare(t)>0?1:-1)});return e.diffSet?.filter((e=>"equal"!==e.state||e.name1!==e.name2))||[]}async generateAssetFilesDiff(e,t){const o=new p,n=o.diff_linesToChars_(e,t),i=o.diff_main(n.chars1,n.chars2,!1);o.diff_charsToLines_(i,n.lineArray);let r=0;return i.map((e=>{const[t,o]=e,n=o.endsWith("\n")?o.split("\n").length-1:o.split("\n").length,i=o.split("\n").map(((e,o)=>({lineContent:e,lineNumber:r+o+1,lineType:t}))).slice(0,n);return-1!==t&&(r+=n),i})).flat()}async generateFilesDiff(e){if("equal"===e.state){const t=this.pathToPosix(w.join(".",e.relativePath,e.name1??"")),o=this.pathToPosix(w.join(".",e.relativePath,e.name2??""));return this.diffFileTemplate(`<span class="renamed"\n >Renamed <span class="from">${t}</span> -> <span class="to">${o}</span></span\n >`,this.diffTableTemplate(this.diffLineMessageTemplate("No changes")))}const t=e.path1&&e.name1?w.join(e.path1,e.name1):null,o=e.path2&&e.name2?w.join(e.path2,e.name2):null,n=this.pathToPosix(w.join(".",e.relativePath,(e.name1||e.name2)??""));if("left"===e.state&&t)return this.diffFileTemplate(`<span class="removed">Removed ${n}</span>`,this.diffTableTemplate(this.diffLineMessageTemplate("File removed")));if("right"===e.state&&o)return this.diffFileTemplate(`<span class="added">Added ${n}</span>`,this.diffTableTemplate(this.diffLineMessageTemplate("File added")));if("distinct"===e.state&&t&&o){const i=this.pathToPosix(w.join(".",e.relativePath,e.name1??"")),r=this.pathToPosix(w.join(".",e.relativePath,e.name2??"")),s=e.name1!==e.name2?`<span class="renamed"\n >Renamed <span class="from">${i}</span> -> <span class="to">${r}</span></span\n >`:n;return this.diffFileTemplate(s,await f.isBinaryFile(t)||await f.isBinaryFile(o)?this.diffTableTemplate(this.diffLineMessageTemplate("Binary file")):this.getDiffTableHTML(await this.generateAssetFilesDiff(y.readFileSync(t,"utf-8"),y.readFileSync(o,"utf-8"))))}return""}async generateChangelog(){this.logger.info(s.colors.green("Generating changelog"));const e=await this.generateAssetFoldersDiff(),t=(await Promise.all(e.map((e=>this.generateFilesDiff(e))))).join("");this.logger.info(s.colors.green("Writing changelog file"));const o=this.changelogTemplate.split("%FILES%");o.splice(1,0,t);const n=o.join("");y.writeFileSync(w.join(this.destinationPath,this.changelogFilename),n),this.logger.info(s.colors.green(`Changelog file written to ${w.join(this.destinationPath,this.changelogFilename)}`))}}class D{sourceDir;outputDir;fontName;constructor(e){this.sourceDir=e.sourceDir,this.outputDir=e.outputDir,this.fontName=e.fontName}async generate(){await m({src:this.sourceDir,dist:this.outputDir,fontName:this.fontName,css:!0,typescript:!0,startUnicode:59905,svgicons2svgfont:{fontHeight:1024}})}}const E=n.cac("pp-dev");let q=global.__pp_dev_profile_session,j=0;const z=e=>{if(q)return new Promise(((t,o)=>{q.post("Profiler.stop",((n,{profile:i})=>{if(n)o(n);else{const o=w.resolve(`./pp-dev-profile-${j++}.cpuprofile`);y.writeFileSync(o,JSON.stringify(i)),e(s.colors.yellow(`CPU profile written to ${s.colors.white(s.colors.dim(o))}`)),q=void 0,t()}}))}))},I=e=>{for(const[t,o]of Object.entries(e))Array.isArray(o)&&(e[t]=o[o.length-1])};function R(e){const t={...e};return delete t["--"],delete t.c,delete t.config,delete t.base,delete t.l,delete t.logLevel,delete t.clearScreen,delete t.d,delete t.debug,delete t.f,delete t.filter,delete t.m,delete t.mode,t}E.option("-c, --config <file>","[string] use specified config file").option("--base <path>","[string] public base path (default: /)").option("-l, --logLevel <level>","[string] info | warn | error | silent").option("--clearScreen","[boolean] allow/disable clear screen when logging").option("-d, --debug [feat]","[string | boolean] show debug logs").option("-f, --filter <filter>","[string] filter debug logs").option("-m, --mode <mode>","[string] set env mode"),E.command("[root]","start dev server").alias("serve").alias("dev").option("--host [host]","[string] specify hostname").option("--port <port>","[number] specify port").option("--https","[boolean] use TLS + HTTP/2").option("--open [path]","[boolean | string] open browser on startup").option("--cors","[boolean] enable CORS").option("--strictPort","[boolean] exit if specified port is already in use").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle").action((async(e,t)=>{I(t);const{createServer:n}=await import("vite");try{const a=await i.loadConfigFromFile({mode:t.mode||"development",command:"serve"},t.config,e,t.logLevel);let l=await r.getViteConfig();const c=i.loadEnv(t.mode||"development",e??$.cwd(),"");if(c&&Object.keys(c).forEach((e=>{e.startsWith("MI_")&&($.env[e]=c[e])})),a){const{plugins:e,...t}=a.config;l=i.mergeConfig(l,t)}const d=await n(i.mergeConfig(l,{root:e,base:t.base,mode:t.mode,configFile:t.config,logLevel:t.logLevel,clearScreen:t.clearScreen,optimizeDeps:{force:t.force},server:R(t),customLogger:s.createLogger(t.logLevel)},!0));if(!d.config.base||"/"===d.config.base)throw new Error('base cannot be equal to "/" or empty string');if(!d.httpServer)throw new Error("HTTP server not available");await d.listen();const p=s.createLogger(t.logLevel),f=global.__pp_dev_start_time??!1,g=f?s.colors.dim(`ready in ${s.colors.reset(s.colors.bold(Math.ceil(o.performance.now()-f)))} ms`):"";p.info(`\n ${s.colors.green(`${s.colors.bold("PP-DEV")} v${r.VERSION}`)} ${g}\n`),d.printUrls(),function(e,t){if(!e.httpServer||!process.stdin.isTTY||process.env.CI)return;e._shortcutsOptions=t;const o=s.createLogger();t.print&&o.info(s.colors.dim(s.colors.green(" ā"))+s.colors.dim(" press ")+s.colors.bold("h")+s.colors.dim(" to show help"));const n=(t.customShortcuts??[]).filter(S).concat(C);let i=!1;const r=async t=>{if(""===t||""===t)return void await e.close().finally((()=>process.exit(1)));if(i)return;"h"===t&&o.info(["",s.colors.bold(" Shortcuts"),...n.map((e=>s.colors.dim(" press ")+s.colors.bold(e.key)+s.colors.dim(` to ${e.description}`)))].join("\n"));const r=n.find((e=>e.key===t));r&&(i=!0,await r.action(e),i=!1)};process.stdin.setRawMode(!0),process.stdin.on("data",r).setEncoding("utf8").resume(),e.httpServer.on("close",(()=>{process.stdin.off("data",r).pause()}))}(d,{print:!0,customShortcuts:[q&&{key:"p",description:"start/stop the profiler",async action(e){if(q)await z(p.info);else{const e=await import("node:inspector").then((e=>e.default));await new Promise((t=>{q=new e.Session,q.connect(),q.post("Profiler.enable",(()=>{q?.post("Profiler.start",(()=>{p.info("Profiler started"),t()}))}))}))}}},{key:"l",description:"proxy re-login",action(e){e.ws.send({type:"custom",event:"redirect",data:{url:`/auth/index/logout?proxyRedirect=${encodeURIComponent("/")}`}})}}]})}catch(e){const o=s.createLogger(t.logLevel);o.error(s.colors.red(`error when starting dev server:\n${e.stack}`),{error:e}),z(o.info),$.exit(1)}})),E.command("next [root]","start dev server").alias("next-serve").alias("next-dev").option("--host [host]","[string] specify hostname").option("--port <port>","[number] specify port",{default:3e3}).option("--https","[boolean] use TLS + HTTP/2").option("--open [path]","[boolean | string] open browser on startup").option("--cors","[boolean] enable CORS").option("--strictPort","[boolean] exit if specified port is already in use").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle").action((async(e,t)=>{I(t);const{default:n}=await import("next"),l=s.createLogger(),c=function(e="info"){const t=L.default(),o=P.createLogger({level:e,format:P.format.cli({level:!0}),transports:[new P.transports.Console]});t.config={logger:o};const n=t.listen;let i;return t.listen=function(...e){i=n.apply(this,e)},t.printUrls=function(e){if(!i)throw new Error("Server is not listening");const t=e=>s.colors.cyan(e.replace(/:(\d+)\//,((e,t)=>`:${s.colors.bold(t)}/`))),n=i.address();if(n&&"object"==typeof n)if("::"===n.address){const i=new a.URL(e||"",`http://localhost:${n.port}`);o.info(` ${s.colors.green("ā")} ${s.colors.bold("Local")}: ${t(i.toString())}`)}else{const i=new a.URL(e||"",`http://[${n.address}]:${n.port}`);o.info(` ${s.colors.green("ā")} ${s.colors.bold("Local")}: ${t(i.toString())}`)}},t}(t.logLevel),d=R(t),p=i.loadEnv(t.mode||"development",e??$.cwd(),"");p&&Object.keys(p).forEach((e=>{e.startsWith("MI_")&&($.env[e]=p[e])}));const f=n({dev:!0,hostname:d.host,port:d.port});await f.prepare();const g=await f.getServer();let h=g.nextConfig.basePath;const{assetPrefix:u}=g.nextConfig;if(h.endsWith("/")||(h+="/"),"/"===h)throw new Error('basePath cannot be equal to "/" or empty string');const m=h.substring(0,h.lastIndexOf("/")),b=g.nextConfig.serverRuntimeConfig.templateName,v=g.nextConfig.serverRuntimeConfig.ppDevConfig,{backendBaseURL:w,portalPageId:y,appId:T,templateLess:x=!0,enableProxyCache:F=!0,miHudLess:S=!0,proxyCacheTTL:C=6e5,disableSSLValidation:k=!1,v7Features:A=!1,personalAccessToken:D=$.env.MI_ACCESS_TOKEN}=v,E=T??y;if(c.use(s.initPPRedirect(h,b)),w){let e;try{e=new URL(w).host}catch(e){l.error(s.colors.red(`Invalid backendBaseURL: ${w}`)),$.exit(1)}const t=new s.MiAPI(w,{headers:{host:e,referer:w,origin:w.replace(/^(https?:\/\/)([^/]+)(\/.*)?$/i,"$1$2")},portalPageId:E,templateLess:x,disableSSLValidation:k,v7Features:A,personalAccessToken:D});if(F){let e=+C;(!e||Number.isNaN(e)||e<0)&&(e=6e5),c.use(s.initProxyCache({devServer:c,ttl:e}))}const o=["/@vite","/@metricinsights","/@",m];u&&o.push(u),c.use(s.initProxy({devServer:c,baseURL:w,proxyIgnore:o,disableSSLValidation:k,miAPI:t}));const n=new RegExp(`^((${h})|/)$`);c.use(s.initLoadPPData(n,t,v)),c.use(s.initRewriteResponse((e=>n.test(s.cutUrlParams(e))),((o,n)=>Buffer.from(s.urlReplacer(e,n.headers.host??"",t.buildPage(o,S)))))),s.internalServer.post("/@api/login",(async(e,o,n)=>{const{token:i,tokenType:r}=e.body;if(!i)return void o.status(400).json({error:"Token is required"}).end();const a=e=>(l.error(e),n(e),null);if("personal"===r){if(!await t.get("/data/page/index/auth/info",{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${i}`},!0).then((async e=>{if("number"==typeof e.data?.user?.user_id)return t.personalAccessToken=i,e;o.status(400).json({error:"Token expired or invalid"}).end()})).catch(a))return;s.redirect(o,"/",302)}else if("regular"===r){if(!await t.get("/api/user",{"Content-Type":"application/json",Accept:"application/json",Token:i},!0).then((e=>{if(e.data?.users?.length)return t.personalAccessToken=void 0,o.setHeader("set-cookie",e.headers["set-cookie"]??""),e;o.status(400).json({error:"Token expired or invalid"}).end()})).catch(a))return;s.redirect(o,"/",302)}})),c.use(s.internalServer)}const q=f.getRequestHandler();c.all("*",((e,t)=>{try{if(e.url?.startsWith(u)&&u!==m){const o=e.url.replace(u,m),n=a.parse(o,!0);return n.pathname?q(e,t,n):(t.statusCode=400,void t.end("Invalid URL"))}const o=a.parse(e.url||"/",!0);if(!o.pathname)return t.statusCode=400,void t.end("Invalid URL");q(e,t,o)}catch(e){const o=e instanceof Error?e.message:"Unknown error";l.error(s.colors.red(`Error handling request: ${o}`)),t.statusCode=500,t.end("Internal Server Error")}}));try{await new Promise((e=>{d.host?e(c.listen(d.port,d.host,(()=>{}))):e(c.listen(d.port,(()=>{})))}));const e=global.__pp_dev_start_time??!1,t=e?s.colors.dim(`ready in ${s.colors.reset(s.colors.bold(Math.ceil(o.performance.now()-e)))} ms`):"";l.info(`\n ${s.colors.green(`${s.colors.bold("PP-DEV")} v${r.VERSION}`)} ${t}\n`,{clear:!0}),c.printUrls(h)}catch(e){const o=s.createLogger(t.logLevel);o.error(s.colors.red(`error when starting dev server:\n${e.stack}`),{error:e}),z(o.info),$.exit(1)}})),E.command("build [root]","build for production").option("--target <target>","[string] transpile target (default: 'modules')").option("--outDir <dir>","[string] output directory (default: dist)").option("--assetsDir <dir>","[string] directory under outDir to place assets in (default: assets)").option("--assetsInlineLimit <number>","[number] static asset base64 inline threshold in bytes (default: 4096)").option("--ssr [entry]","[string] build specified entry for server-side rendering").option("--sourcemap [output]",'[boolean | "inline" | "hidden"] output source maps for build (default: false)').option("--minify [minifier]",'[boolean | "terser" | "esbuild"] enable/disable minification, or specify minifier to use (default: esbuild)').option("--manifest [name]","[boolean | string] emit build manifest json").option("--ssrManifest [name]","[boolean | string] emit ssr manifest json").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle (experimental)").option("--emptyOutDir","[boolean] force empty outDir when it's outside of root").option("-w, --watch","[boolean] rebuilds when modules have changed on disk").option("--changelog [assetsFile]","[boolean | string] generate changelog between assetsFile and current build (default: false)").action((async(e,t)=>{I(t);const o=R(t);try{const n=await i.loadConfigFromFile({mode:t.mode||"production",command:"build"},t.config,e,t.logLevel);let a=await r.getViteConfig();if(n){const{plugins:e,...t}=n.config;a=i.mergeConfig(a,t)}const l=i.mergeConfig(a,{root:e,base:t.base,mode:t.mode,configFile:t.config,logLevel:t.logLevel,clearScreen:t.clearScreen,optimizeDeps:{force:t.force},build:o},!0);if(await i.build(l),o.changelog){const n=e||$.cwd(),i=l.build?.outDir||"dist";let r="";if("string"==typeof o.changelog)r=w.resolve(n,o.changelog);else{const e=w.resolve(n,l.ppDevConfig?.syncBackupsDir||"backups");if(!y.existsSync(e))return void s.createLogger(t.logLevel).warn(s.colors.yellow("backups directory not found, skipping changelog generation"));const o=y.readdirSync(e,{withFileTypes:!0});if(!o.length)return void s.createLogger(t.logLevel).warn(s.colors.yellow("no backups found, skipping changelog generation"));const i=o.filter((e=>e.isFile()&&e.name.endsWith(".zip"))).reduce(((t,o)=>y.statSync(w.resolve(e,t.name)).mtimeMs>y.statSync(w.resolve(e,o.name)).mtimeMs?t:o),o[0]).name;r=w.resolve(e,i)}const a=w.resolve(n,i);let c="dist-zip";l.ppDevConfig&&(!1===l.ppDevConfig.distZip?c=l.build?.outDir||"dist":"object"==typeof l.ppDevConfig.distZip&&"string"==typeof l.ppDevConfig.distZip.outDir&&(c=l.ppDevConfig.distZip.outDir));const d=new A({oldAssetsPath:r,newAssetsPath:a,destinationPath:w.resolve(n,c)});await d.generateChangelog()}}catch(e){s.createLogger(t.logLevel).error(s.colors.red(`error during build:\n${e.stack}`),{error:e}),$.exit(1)}finally{z((e=>s.createLogger(t.logLevel).info(e)))}})),E.command("changelog [oldAssetPath] [newAssetPath]","generate changelog between two assets files/folders").option("--oldAssetsPath <oldAssetsPath>","[string] path to the old assets zip file or folder").option("--newAssetsPath <newAssetsPath>","[string] path to the new assets zip file or folder").option("--destination <destination>","[string] destination folder for the changelog (default: .)").option("--filename <filename>","[string] filename for the changelog (default: CHANGELOG.html)").action((async(e,t,o)=>{I(o);const{oldAssetsPath:n=e,newAssetsPath:i=t,destination:r=".",filename:a="CHANGELOG.html",logLevel:l}=o,c=$.cwd();n&&i||(s.createLogger(l).error(s.colors.red("error during changelog generation: oldAssetPath and newAssetPath are required")),$.exit(1));const d=w.resolve(c,n),p=w.resolve(c,i),f=w.resolve(c,r),g=new A({oldAssetsPath:d,newAssetsPath:p,destinationPath:f,changelogFilename:a});await g.generateChangelog()})),E.command("generate-icon-font [source] [destination]","generate icon font from SVG files").option("--source <source>","[string] path to the source directory with SVG files").option("--destination <destination>","[string] path to the destination directory to save the generated font files").option("--font-name, -n <fontName>","[string] name of the font to generate (default: 'icon-font')").action((async(e,t,o)=>{I(o);const{source:n=e,destination:i=t,fontName:r="icon-font"}=o,a=$.cwd(),l=w.resolve(a,n),c=w.resolve(a,i),d=new D({sourceDir:l,outputDir:c,fontName:r}),p=s.createLogger(o.logLevel);p.info(`Generating icon font from SVG files in ${s.colors.dim(l)}`),await d.generate(),p.info(`Icon font generated and saved to ${s.colors.dim(c)}`)})),E.command("optimize [root]","pre-bundle dependencies").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle").action((async(e,t)=>{I(t);try{const o=await i.loadConfigFromFile({mode:t.mode||"production",command:"build"},t.config,e,t.logLevel);let n=await r.getViteConfig();if(o){const{plugins:e,...t}=o.config;n=i.mergeConfig(n,t)}const s=await i.resolveConfig(i.mergeConfig(n,{root:e,base:t.base,configFile:t.config,logLevel:t.logLevel,mode:t.mode}),"serve");await i.optimizeDeps(s,t.force,!0)}catch(e){s.createLogger(t.logLevel).error(s.colors.red(`error when optimizing deps:\n${e.stack}`),{error:e}),$.exit(1)}})),E.command("preview [root]","locally preview production build").option("--host [host]","[string] specify hostname").option("--port <port>","[number] specify port").option("--strictPort","[boolean] exit if specified port is already in use").option("--https","[boolean] use TLS + HTTP/2").option("--open [path]","[boolean | string] open browser on startup").option("--outDir <dir>","[string] output directory (default: dist)").action((async(e,t)=>{I(t);try{const o=await i.loadConfigFromFile({mode:t.mode||"production",command:"build"},t.config,e,t.logLevel);let n=await r.getViteConfig();if(o){const{plugins:e,...t}=o.config;n=i.mergeConfig(n,t)}(await i.preview(i.mergeConfig(n,{root:e,base:t.base,configFile:t.config,logLevel:t.logLevel,mode:t.mode,build:{outDir:t.outDir},preview:{port:t.port,strictPort:t.strictPort,host:t.host,https:t.https,open:t.open}}))).printUrls()}catch(e){s.createLogger(t.logLevel).error(s.colors.red(`error when starting preview server:\n${e.stack}`),{error:e}),$.exit(1)}finally{z((e=>s.createLogger(t.logLevel).info(e)))}})),E.help(),E.version(r.VERSION),E.parse(),exports.stopProfiler=z;
|
|
1
|
+
"use strict";const e=require("path"),t=require("fs"),o=require("node:perf_hooks"),n=require("cac"),i=require("vite"),r=require("./index-DGgPiX1I.js"),s=require("./plugin-CqSS_2nh.js"),a=require("url"),l=require("dir-compare"),c=require("diff-match-patch"),d=require("isbinaryfile"),p=require("os"),f=require("crypto"),h=require("extract-zip"),g=require("svgtofont");function u(e){const t=Object.create(null);if(e)for(const o in e)if("default"!==o){const n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:()=>e[o]})}return t.default=e,Object.freeze(t)}require("ejs"),require("http-proxy-middleware"),require("picocolors"),require("axios"),require("jsdom"),require("https"),require("memory-cache"),require("process"),require("child_process"),require("console"),require("zlib"),require("express");const m=u(e),b=u(t),w=u(l),v=u(p),y=u(f);function x(e){return null!=e&&!1!==e}const P=[{key:"r",description:"restart the server",async action(e){await e.restart()}},{key:"u",description:"show server url",action(e){e.config.logger.info(""),e.printUrls()}},{key:"o",description:"open in browser",action(e){e.openBrowser()}},{key:"c",description:"clear console",action(e){e.config.logger.clearScreen("error")}},{key:"q",description:"quit",async action(e){await e.close().finally((()=>process.exit()))}},{key:"C",description:"clear proxy cache",action(e){e.cache&&(e.cache.clear(),e.config.logger.info("Proxy cache cleared"))}}];const L=/(.+)(-[a-f0-9]{6,20})(\.[a-z0-9]+)$/i;class T{oldAssetsPath;newAssetsPath;destinationPath;changelogFilename;changelogTemplate='<!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8" />\n <title>Changelog Diff</title>\n <style>\n tr,\n td {\n padding: 0;\n }\n .diff-file {\n margin-top: 20px;\n border: 1px solid #e1e4e8;\n border-radius: 6px;\n }\n .diff-file-title {\n padding: 10px 20px;\n background-color: #f6f8fa;\n border-bottom: 1px solid #e1e4e8;\n border-radius: 6px 6px 0 0;\n font-weight: bold;\n }\n .diff-file-title .renamed {\n font-weight: normal;\n }\n .diff-file-title .renamed .from {\n color: #cb2431;\n }\n .diff-file-title .renamed .to {\n color: #22863a;\n }\n .diff-file-title .added {\n color: #22863a;\n }\n .diff-file-title .deleted {\n color: #cb2431;\n }\n .diff-file-content {\n }\n .diff-table {\n tab-size: 8;\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n }\n .blob-num {\n position: relative;\n color: #1f2328;\n width: 1%;\n min-width: 50px;\n padding: 0 10px;\n font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;\n font-size: 12px;\n line-height: 20px;\n text-align: right;\n white-space: nowrap;\n vertical-align: top;\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n }\n .blob-num.addition {\n background-color: #ccffd8;\n border-color: #1f883e;\n }\n .blob-num.deletion {\n background-color: #ffd7d5;\n border-color: #cf222e;\n }\n .blob-num::before {\n content: attr(data-line-number);\n }\n .blob-code {\n position: relative;\n padding: 0 10px 0 22px;\n vertical-align: top;\n color: #1f2329;\n }\n .blob-code.code-addition {\n background-color: #e6ffec;\n }\n .blob-code.code-deletion {\n background-color: #ffebe9;\n }\n .blob-code.skip,\n .blob-code.message {\n text-align: center;\n }\n .blob-code.skip .blob-code-inner,\n .blob-code.message .blob-code-inner {\n font-weight: bold;\n color: #6a737d;\n padding: 10px 0;\n }\n .blob-code-inner {\n display: table-cell;\n overflow: visible;\n font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;\n font-size: 12px;\n word-wrap: anywhere;\n white-space: pre-wrap;\n }\n .blob-code-inner::before {\n content: attr(data-code-prefix);\n position: absolute;\n top: 1px;\n left: 8px;\n padding-right: 8px;\n }\n </style>\n </head>\n <body>\n <h1>Changelog Diff</h1>\n\n %FILES%\n </body>\n </html>';diffFileTemplateHandler;diffLineTemplateHandler;contextLines=3;logger;constructor(e){const{oldAssetsPath:t,newAssetsPath:o,destinationPath:n,changelogTemplate:i,diffFileTemplateHandler:r,diffLineTemplateHandler:a,changelogFilename:l,contextLines:c}=e;if(this.logger=s.createLogger(),!t||!o||!n)throw new Error("Previous assets path, current assets path and destination path are required");if(t===o)throw new Error("Previous and current assets paths must be different");if(!this.isExists(t))throw new Error(`Previous assets path ${t} does not exist`);if(!this.isExists(o))throw new Error(`Current assets path ${o} does not exist`);if(this.isZipFile(t)){const e=m.resolve(v.tmpdir(),y.createHash("md5").update(t).digest("hex"));this.oldAssetsPath=this.unzipFile(t,e).then((()=>this.normalizeAssetFolderPath(e))).then((e=>{if(this.isEmptyFolder(e))throw new Error(`Previous assets path ${e} is empty`);return e}))}else{if(!this.isFolder(t))throw new Error(`Invalid previous assets path ${t}. It must be a folder or a zip file`);{const e=this.normalizeAssetFolderPath(t);if(this.isEmptyFolder(e))throw new Error(`Previous assets path ${e} is empty`);this.oldAssetsPath=Promise.resolve(e)}}if(this.isZipFile(o)){const e=m.resolve(v.tmpdir(),y.createHash("md5").update(o).digest("hex"));this.newAssetsPath=this.unzipFile(o,e).then((()=>this.normalizeAssetFolderPath(e))).then((e=>{if(this.isEmptyFolder(e))throw new Error(`Current assets path ${e} is empty`);return e}))}else{if(!this.isFolder(o))throw new Error(`Invalid current assets path ${o}. It must be a folder or a zip file`);{const e=this.normalizeAssetFolderPath(o);if(this.isEmptyFolder(e))throw new Error(`Current assets path ${o} is empty`);this.newAssetsPath=Promise.resolve(e)}}this.destinationPath=n,this.mkdirpSync(this.destinationPath),this.changelogFilename=l||"CHANGELOG.html",i&&(this.templateIsValid(i)?this.changelogTemplate=i:this.logger.warn(s.colors.yellow("Invalid changelog template, using default"))),"function"==typeof r&&(this.diffFileTemplateHandler=r),"function"==typeof a&&(this.diffLineTemplateHandler=a),c&&(this.contextLines=c)}templateIsValid(e){return e.includes("%FILES%")}isExists(e){return b.existsSync(e)}isZipFile(e){return e.endsWith(".zip")}isFolder(e){return b.lstatSync(e).isDirectory()}isEmptyFolder(e){return 0===b.readdirSync(e,{withFileTypes:!0}).length}mkdirpSync(e){b.existsSync(e)||b.mkdirSync(e,{recursive:!0})}async unzipFile(e,t){return b.rmSync(t,{force:!0,recursive:!0}),h(e,{dir:t})}normalizeAssetFolderPath(e){const t=b.readdirSync(e);return 1===t.length&&b.lstatSync(m.join(e,t[0])).isDirectory()?this.normalizeAssetFolderPath(m.join(e,t[0])):e}pathToPosix(e){return e.replace(/\\/g,"/")}diffTableTemplate(e){return`<table class="diff-table">\n <tbody>\n ${e}\n </tbody>\n </table>`}getDiffTableHTML(e){const t=e.filter(((e,t,o)=>0!==e.lineType||(t>=0&&t<this.contextLines||t>o.length-(this.contextLines+1)&&t<=o.length-1||o.slice(t-this.contextLines,t+this.contextLines+1).some((e=>0!==e.lineType))))).map(((e,t,o)=>{if(t>0){const n=o[t-1];if(e.lineNumber-n.lineNumber>1)return[{lineNumber:-1,lineContent:"",lineType:0},e]}return[e]})).flat().map((e=>this.diffLineHTML(e))).join("");return this.diffTableTemplate(t)}diffLineMessageTemplate(e){return`<tr>\n <td class="blob-num"></td>\n <td class="blob-num"></td>\n <td class="blob-code message">\n <span class="blob-code-inner">${e}</span>\n </td>\n </tr>`}diffLineSkipTemplate(){return'<tr>\n <td class="blob-num"></td>\n <td class="blob-num"></td>\n <td class="blob-code skip">\n <span class="blob-code-inner">Skip</span>\n </td>\n </tr>'}diffLineTemplate(e){const{lineNumber:t,lineContent:o,lineType:n}=e,i=1===n?"addition":-1===n?"deletion":"";return`<tr>\n <td\n class="blob-num ${i}${"addition"===i?" empty":""}"\n ${"addition"!==i?` data-line-number="${t}"`:""}\n ></td>\n <td\n class="blob-num ${i}${"deletion"===i?" empty":""}"\n ${"deletion"!==i?` data-line-number="${t}"`:""}\n ></td>\n <td class="blob-code ${1===n?"code-addition":-1===n?"code-deletion":""}">\n <span class="blob-code-inner" data-code-prefix="${1===n?"+":-1===n?"-":" "}">${r=o??"",r.replace(/[\u00A0-\u9999<>&]/g,(e=>"&#"+e.charCodeAt(0)+";"))}</span>\n </td>\n </tr>`;var r}diffLineHTML(e){if(this.diffLineTemplateHandler)return this.diffLineTemplateHandler(e);const{lineNumber:t}=e;return-1===t?this.diffLineSkipTemplate():this.diffLineTemplate(e)}diffFileTemplate(e,t){return this.diffFileTemplateHandler?this.diffFileTemplateHandler(e,t):`<div class="diff-file">\n <div class="diff-file-title">${e}</div>\n <div class="diff-file-content">${t}</div>\n </div>`}async generateAssetFoldersDiff(){this.logger.info(s.colors.blue(`Comparing asset folders ${await this.oldAssetsPath} and ${await this.newAssetsPath}`));const e=await w.compare(await this.oldAssetsPath,await this.newAssetsPath,{compareContent:!0,skipSymlinks:!0,compareSize:!0,compareDate:!1,compareNameHandler:(e,t)=>(L.test(e)&&(e=e.replace(L,"$1$3")),L.test(t)&&(t=t.replace(L,"$1$3")),0===e.localeCompare(t)?0:e.localeCompare(t)>0?1:-1)});return e.diffSet?.filter((e=>"equal"!==e.state||e.name1!==e.name2))||[]}async generateAssetFilesDiff(e,t){const o=new c,n=o.diff_linesToChars_(e,t),i=o.diff_main(n.chars1,n.chars2,!1);o.diff_charsToLines_(i,n.lineArray);let r=0;return i.map((e=>{const[t,o]=e,n=o.endsWith("\n")?o.split("\n").length-1:o.split("\n").length,i=o.split("\n").map(((e,o)=>({lineContent:e,lineNumber:r+o+1,lineType:t}))).slice(0,n);return-1!==t&&(r+=n),i})).flat()}async generateFilesDiff(e){if("equal"===e.state){const t=this.pathToPosix(m.join(".",e.relativePath,e.name1??"")),o=this.pathToPosix(m.join(".",e.relativePath,e.name2??""));return this.diffFileTemplate(`<span class="renamed"\n >Renamed <span class="from">${t}</span> -> <span class="to">${o}</span></span\n >`,this.diffTableTemplate(this.diffLineMessageTemplate("No changes")))}const t=e.path1&&e.name1?m.join(e.path1,e.name1):null,o=e.path2&&e.name2?m.join(e.path2,e.name2):null,n=this.pathToPosix(m.join(".",e.relativePath,(e.name1||e.name2)??""));if("left"===e.state&&t)return this.diffFileTemplate(`<span class="removed">Removed ${n}</span>`,this.diffTableTemplate(this.diffLineMessageTemplate("File removed")));if("right"===e.state&&o)return this.diffFileTemplate(`<span class="added">Added ${n}</span>`,this.diffTableTemplate(this.diffLineMessageTemplate("File added")));if("distinct"===e.state&&t&&o){const i=this.pathToPosix(m.join(".",e.relativePath,e.name1??"")),r=this.pathToPosix(m.join(".",e.relativePath,e.name2??"")),s=e.name1!==e.name2?`<span class="renamed"\n >Renamed <span class="from">${i}</span> -> <span class="to">${r}</span></span\n >`:n;return this.diffFileTemplate(s,await d.isBinaryFile(t)||await d.isBinaryFile(o)?this.diffTableTemplate(this.diffLineMessageTemplate("Binary file")):this.getDiffTableHTML(await this.generateAssetFilesDiff(b.readFileSync(t,"utf-8"),b.readFileSync(o,"utf-8"))))}return""}async generateChangelog(){this.logger.info(s.colors.green("Generating changelog"));const e=await this.generateAssetFoldersDiff(),t=(await Promise.all(e.map((e=>this.generateFilesDiff(e))))).join("");this.logger.info(s.colors.green("Writing changelog file"));const o=this.changelogTemplate.split("%FILES%");o.splice(1,0,t);const n=o.join("");b.writeFileSync(m.join(this.destinationPath,this.changelogFilename),n),this.logger.info(s.colors.green(`Changelog file written to ${m.join(this.destinationPath,this.changelogFilename)}`))}}class F{sourceDir;outputDir;fontName;constructor(e){this.sourceDir=e.sourceDir,this.outputDir=e.outputDir,this.fontName=e.fontName}async generate(){await g({src:this.sourceDir,dist:this.outputDir,fontName:this.fontName,css:!0,typescript:!0,startUnicode:59905,svgicons2svgfont:{fontHeight:1024}})}}const $=n.cac("pp-dev");let S=global.__pp_dev_profile_session,C=0;const j=e=>{if(S)return new Promise(((t,o)=>{S.post("Profiler.stop",((n,{profile:i})=>{if(n)o(n);else{const o=m.resolve(`./pp-dev-profile-${C++}.cpuprofile`);b.writeFileSync(o,JSON.stringify(i)),e(s.colors.yellow(`CPU profile written to ${s.colors.white(s.colors.dim(o))}`)),S=void 0,t()}}))}))},D=e=>{for(const[t,o]of Object.entries(e))Array.isArray(o)&&(e[t]=o[o.length-1])};function A(e){const t={...e};return delete t["--"],delete t.c,delete t.config,delete t.base,delete t.l,delete t.logLevel,delete t.clearScreen,delete t.d,delete t.debug,delete t.f,delete t.filter,delete t.m,delete t.mode,t}$.option("-c, --config <file>","[string] use specified config file").option("--base <path>","[string] public base path (default: /)").option("-l, --logLevel <level>","[string] info | warn | error | silent").option("--clearScreen","[boolean] allow/disable clear screen when logging").option("-d, --debug [feat]","[string | boolean] show debug logs").option("-f, --filter <filter>","[string] filter debug logs").option("-m, --mode <mode>","[string] set env mode"),$.command("[root]","start dev server").alias("serve").alias("dev").option("--host [host]","[string] specify hostname").option("--port <port>","[number] specify port").option("--https","[boolean] use TLS + HTTP/2").option("--open [path]","[boolean | string] open browser on startup").option("--cors","[boolean] enable CORS").option("--strictPort","[boolean] exit if specified port is already in use").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle").action((async(e,t)=>{D(t);const{createServer:n}=await import("vite");try{const a=await i.loadConfigFromFile({mode:t.mode||"development",command:"serve"},t.config,e,t.logLevel);let l=await r.getViteConfig();const c=i.loadEnv(t.mode||"development",e??process.cwd(),"");if(c&&Object.keys(c).forEach((e=>{e.startsWith("MI_")&&(process.env[e]=c[e])})),a){const{plugins:e,...t}=a.config;l=i.mergeConfig(l,t)}const d=await n(i.mergeConfig(l,{root:e,base:t.base,mode:t.mode,configFile:t.config,logLevel:t.logLevel,clearScreen:t.clearScreen,optimizeDeps:{force:t.force},server:A(t),customLogger:s.createLogger(t.logLevel)},!0));if(!d.config.base||"/"===d.config.base)throw new Error('base cannot be equal to "/" or empty string');if(!d.httpServer)throw new Error("HTTP server not available");await d.listen();const p=s.createLogger(t.logLevel),f=global.__pp_dev_start_time??!1,h=f?s.colors.dim(`ready in ${s.colors.reset(s.colors.bold(Math.ceil(o.performance.now()-f)))} ms`):"";p.info(`\n ${s.colors.green(`${s.colors.bold("PP-DEV")} v${r.VERSION}`)} ${h}\n`),d.printUrls(),function(e,t){if(!e.httpServer||!process.stdin.isTTY||process.env.CI)return;e._shortcutsOptions=t;const o=s.createLogger();t.print&&o.info(s.colors.dim(s.colors.green(" ā"))+s.colors.dim(" press ")+s.colors.bold("h")+s.colors.dim(" to show help"));const n=(t.customShortcuts??[]).filter(x).concat(P);let i=!1;const r=async t=>{if(""===t||""===t)return void await e.close().finally((()=>process.exit(1)));if(i)return;"h"===t&&o.info(["",s.colors.bold(" Shortcuts"),...n.map((e=>s.colors.dim(" press ")+s.colors.bold(e.key)+s.colors.dim(` to ${e.description}`)))].join("\n"));const r=n.find((e=>e.key===t));r&&(i=!0,await r.action(e),i=!1)};process.stdin.setRawMode(!0),process.stdin.on("data",r).setEncoding("utf8").resume(),e.httpServer.on("close",(()=>{process.stdin.off("data",r).pause()}))}(d,{print:!0,customShortcuts:[...S?[{key:"p",description:"start/stop the profiler",async action(e){if(S)await j(p.info);else{const e=await import("node:inspector").then((e=>e.default));await new Promise((t=>{S=new e.Session,S.connect(),S.post("Profiler.enable",(()=>{S?.post("Profiler.start",(()=>{p.info("Profiler started"),t()}))}))}))}}}]:[],{key:"l",description:"proxy re-login",action(e){e.ws.send({type:"custom",event:"redirect",data:{url:`/auth/index/logout?proxyRedirect=${encodeURIComponent("/")}`}})}}]})}catch(e){const o=s.createLogger(t.logLevel);o.error(s.colors.red(`error when starting dev server:\n${e.stack}`),{error:e}),j(o.info),process.exit(1)}})),$.command("next [root]","start Next.js development server with pp-dev integration").alias("next-serve").alias("next-dev").option("--host [host]","[string] specify hostname").option("--port <port>","[number] specify port",{default:3e3}).option("--https","[boolean] use TLS + HTTP/2").option("--open [path]","[boolean | string] open browser on startup").option("--cors","[boolean] enable CORS").option("--strictPort","[boolean] exit if specified port is already in use").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle").action((async(e,t)=>{D(t);try{if(!r.isNextAvailable())throw new Error("Next.js is required but not available. Please install Next.js as a dependency:\nnpm install next@^13\n\nThis package requires Next.js >=13 <16 as a peer dependency.");const{next:o}=await r.safeNextImport(),{join:n,basename:l}=await import("path"),{createServer:c}=await import("http"),{default:d}=(await import("next/dist/server/config.js")).default,p=s.createLogger(),f=A(t),h=i.loadEnv(t.mode||"development",e??process.cwd(),"");h&&Object.keys(h).forEach((e=>{e.startsWith("MI_")&&(process.env[e]=h[e])}));const g=e?n(process.cwd(),e):process.cwd();p.info(g);const u=await d("development",g);let m=u?.experimental?.ppDev||u?.ppDev||{};if(0===Object.keys(m).length)try{const{getConfig:e}=await Promise.resolve().then((()=>require("./index-DGgPiX1I.js"))).then((e=>e.config)),t=await e();Object.keys(t).length>0?(m=t,p.info(s.colors.blue("š§ Loaded pp-dev config from standalone config file"))):p.info(s.colors.yellow("ā ļø No pp-dev config found in Next.js config or standalone file, using defaults"))}catch(e){p.info(s.colors.yellow("ā ļø Failed to load standalone pp-dev config, using defaults")),console.debug("Error loading standalone config:",e)}else p.info(s.colors.blue("š§ Loaded pp-dev config from Next.js config"));const{backendBaseURL:b=process.env.MI_BACKEND_URL||"http://localhost:8080",portalPageId:w=parseInt(process.env.MI_PORTAL_PAGE_ID||"1"),templateLess:v=!0,v7Features:y=!0,disableSSLValidation:x=!1,enableProxyCache:P=!0,proxyCacheTTL:L=6e5,personalAccessToken:T=process.env.MI_ACCESS_TOKEN,distZip:F=!1,syncBackupsDir:$="./backups",miHudLess:S=!1}=m;let C=m.templateName;if(!C)try{const{getPkg:e}=await Promise.resolve().then((()=>require("./index-DGgPiX1I.js"))).then((e=>e.config));C=e().name}catch(e){C=l(g)}let j=v?"/p":"/pl";j+=`/${C}`;const D=o({dev:!0,hostname:f.host||"localhost",port:f.port,dir:g,conf:{...u,basePath:j,assetPrefix:j}});if(await D.prepare(),j.endsWith("/")||(j+="/"),"/"===j)throw new Error('basePath cannot be equal to "/" or equal to empty string');j.substring(0,j.lastIndexOf("/"));p.info(s.colors.green("ā
Next.js app prepared successfully")),p.info(s.colors.blue(`š§ pp-dev plugin configured for template: ${C}`)),p.info(s.colors.blue(`š§ Base path configured: ${j}`)),b&&(p.info(s.colors.blue(`š Backend URL: ${b}`)),p.info(s.colors.blue(`š Portal Page ID: ${w}`)));const E=D.getRequestHandler(),k="number"==typeof f.port?f.port:3e3,N="string"==typeof f.host?f.host:"localhost",I=new Set,q=c((async(e,t)=>{try{const o=e.url||"/",n=o.split("?")[0];let i=a.parse(o,!0);if(z.length>0){if(n.startsWith("/_next/")||"/favicon.ico"===n||n.startsWith("/__nextjs_")||n.startsWith("/api/")){if(M.length>0){let c=0;const d=()=>{if(c>=M.length)return void r();const o=M[c];c++,o(e,t,d)};return void d()}return void r()}let s=0;const l=()=>{if(s>=z.length)return void r();const o=z[s];s++,o(e,t,l)};return void l()}async function r(){if(n.startsWith(j)){const t=n.substring(j.length);e.url=t||"/",i=a.parse(t,!0)}else if(n===j.replace(/\/$/,""))e.url="/",i=a.parse("/",!0);else if(n.startsWith("/_next/")||"/favicon.ico"===n||n.startsWith("/__nextjs_"));else if("/"===n)return t.writeHead(302,{Location:j}),void t.end();await E(e,t,i)}r()}catch(p){console.error("[DEBUG] Error:",p),t.statusCode=500,t.end("Internal Server Error")}}));let _=null,z=[],M=[];if(b){const e=new URL(b).host,t={headers:{host:e,referer:b,origin:b.replace(/^(https?:\/\/)([^/]+)(\/.*)?$/i,"$1$2")},portalPageId:w,appId:w,templateLess:v,disableSSLValidation:x,v7Features:y,personalAccessToken:T??process.env.MI_ACCESS_TOKEN};_=new s.MiAPI(b,t);const o=s.initPPRedirect(j,C),n=(e,t,n)=>{o(e,t,n)};if(M.push(n),z.push(n),P){let e=+L;(!e||Number.isNaN(e)||e<0)&&(e=6e5);const t={devServer:{middlewares:{use:e=>e},config:{logger:console}},ttl:e},o=s.initProxyCache(t),n=(e,t,n)=>{o(e,t,n)};z.push(n),p.info(s.colors.blue(`š§ Proxy cache middleware added with TTL: ${e}ms`))}const i=j.endsWith("/")?j.substring(0,j.length-1):j,r={middlewares:{use:e=>e},config:{logger:console}},a=s.initProxy({devServer:r,baseURL:b,proxyIgnore:["/@vite","/@metricinsights","/@",i,"/_next","/favicon.ico","/__nextjs_","/api"],disableSSLValidation:x,miAPI:_}),l=(e,t,o)=>{a(e,t,o)};z.push(l);const c=new RegExp(`^((${j})|/)$`),d=s.initLoadPPData(c,_,{}),f=(e,t,o)=>{d(e,t,o)};z.push(f);const h=s.internalServer,g=(e,t,o)=>{if(e.url?.startsWith("/@api/")){h(e,t,(()=>{}))}else o()};M.push(g),z.push(g);const u=s.initRewriteResponse((e=>e.split("?")[0].endsWith("index.html")),((t,o)=>Buffer.from(s.urlReplacer(e,o.headers.host??"",_.buildPage(t,S))))),m=(e,t,o)=>{u(e,t,o)};z.push(m),p.info(s.colors.blue(`š§ ${z.length} pp-dev middlewares initialized`)),p.info(s.colors.blue(`š§ ${M.length} essential middlewares for internal routes`)),p.info(s.colors.blue(`š§ MiAPI initialized for backend: ${b}`)),p.info(s.colors.blue(`š§ Portal Page ID: ${w}`))}q.listen(k,N,(()=>{p.info(s.colors.green(`ā
pp-dev Next.js server running at http://${N}:${k}`)),p.info(s.colors.blue(`š± Next.js app accessible at http://${N}:${k}${j}`)),p.info(s.colors.blue("š§ Base path handling active")),q.on("connection",(e=>{I.add(e),e.on("close",(()=>I.delete(e)))}));const e=async e=>{p.info(s.colors.yellow(`\nš Received ${e}, shutting down gracefully...`));const t=setTimeout((()=>{p.info(s.colors.yellow("ā° Shutdown timeout reached, forcing exit")),process.exit(0)}),5e3);try{for(const e of Array.from(I))e.destroy();I.clear(),await new Promise((e=>{q.close((()=>{p.info(s.colors.yellow("š HTTP server closed")),e()}))})),D&&"function"==typeof D.close&&(await D.close(),p.info(s.colors.yellow("š Next.js app closed"))),clearTimeout(t),p.info(s.colors.green("ā
Graceful shutdown completed")),process.exit(0)}catch(e){clearTimeout(t),p.error(s.colors.red(`ā Error during graceful shutdown: ${e}`)),process.exit(1)}};let t=process;if("function"!=typeof process.on){const e=globalThis.process||global.process;e&&"function"==typeof e.on&&(t=e,p.info(s.colors.green("ā
Using global process object for event handlers")))}if("function"==typeof t.on)try{t.on("SIGINT",(()=>e("SIGINT"))),t.on("SIGTERM",(()=>e("SIGTERM"))),t.on("uncaughtException",(t=>{p.error(s.colors.red(`ā Uncaught Exception: ${t}`)),e("uncaughtException")})),t.on("unhandledRejection",((t,o)=>{p.error(s.colors.red(`ā Unhandled Rejection at: ${o}, reason: ${t}`)),e("unhandledRejection")})),p.info(s.colors.green("ā
Process event handlers registered successfully"))}catch(e){p.warn(s.colors.yellow(`ā ļø Failed to register process event handlers: ${e}`))}else p.warn(s.colors.yellow("ā ļø process.on is not available, graceful shutdown handlers will not be registered")),p.info(s.colors.blue("š” This might be due to bundling or environment constraints"))}))}catch(e){const t=s.createLogger("error");e instanceof Error&&e.message.includes("Next.js is required")?(t.error(s.colors.red("ā Next.js Peer Dependency Error:")),t.error(s.colors.red(e.message)),t.error(s.colors.yellow("\nš” To fix this issue:")),t.error(s.colors.blue(" 1. Install Next.js in your project:")),t.error(s.colors.white(" npm install next@^15")),t.error(s.colors.blue(" 2. Or use yarn:")),t.error(s.colors.white(" yarn add next@^15")),t.error(s.colors.blue(" 3. Or use pnpm:")),t.error(s.colors.white(" pnpm add next@^15")),t.error(s.colors.yellow("\nš For more information, see:")),t.error(s.colors.blue(" https://nextjs.org/docs/getting-started"))):t.error(s.colors.red(`ā Failed to start Next.js server: ${e}`)),process.exit(1)}})),$.command("build [root]","build for production").option("--target <target>","[string] transpile target (default: 'modules')").option("--outDir <dir>","[string] output directory (default: dist)").option("--assetsDir <dir>","[string] directory under outDir to place assets in (default: assets)").option("--assetsInlineLimit <number>","[number] static asset base64 inline threshold in bytes (default: 4096)").option("--ssr [entry]","[string] build specified entry for server-side rendering").option("--sourcemap [output]",'[boolean | "inline" | "hidden"] output source maps for build (default: false)').option("--minify [minifier]",'[boolean | "terser" | "esbuild"] enable/disable minification, or specify minifier to use (default: esbuild)').option("--manifest [name]","[boolean | string] emit build manifest json").option("--ssrManifest [name]","[boolean | string] emit ssr manifest json").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle (experimental)").option("--emptyOutDir","[boolean] force empty outDir when it's outside of root").option("-w, --watch","[boolean] rebuilds when modules have changed on disk").option("--changelog [assetsFile]","[boolean | string] generate changelog between assetsFile and current build (default: false)").action((async(e,t)=>{D(t);const o=A(t);try{const n=await i.loadConfigFromFile({mode:t.mode||"production",command:"build"},t.config,e,t.logLevel);let a=await r.getViteConfig();if(n){const{plugins:e,...t}=n.config;a=i.mergeConfig(a,t)}const l=i.mergeConfig(a,{root:e,base:t.base,mode:t.mode,configFile:t.config,logLevel:t.logLevel,clearScreen:t.clearScreen,optimizeDeps:{force:t.force},build:o},!0);if(await i.build(l),o.changelog){const n=e||process.cwd(),i=l.build?.outDir||"dist";let r="";if("string"==typeof o.changelog)r=m.resolve(n,o.changelog);else{const e=m.resolve(n,l.ppDevConfig?.syncBackupsDir||"backups");if(!b.existsSync(e))return void s.createLogger(t.logLevel).warn(s.colors.yellow("backups directory not found, skipping changelog generation"));const o=b.readdirSync(e,{withFileTypes:!0});if(!o.length)return void s.createLogger(t.logLevel).warn(s.colors.yellow("no backups found, skipping changelog generation"));const i=o.filter((e=>e.isFile()&&e.name.endsWith(".zip"))).reduce(((t,o)=>b.statSync(m.resolve(e,t.name)).mtimeMs>b.statSync(m.resolve(e,o.name)).mtimeMs?t:o),o[0]).name;r=m.resolve(e,i)}const a=m.resolve(n,i);let c="dist-zip";l.ppDevConfig&&(!1===l.ppDevConfig.distZip?c=l.build?.outDir||"dist":"object"==typeof l.ppDevConfig.distZip&&"string"==typeof l.ppDevConfig.distZip.outDir&&(c=l.ppDevConfig.distZip.outDir));const d=new T({oldAssetsPath:r,newAssetsPath:a,destinationPath:m.resolve(n,c)});await d.generateChangelog()}}catch(e){s.createLogger(t.logLevel).error(s.colors.red(`error during build:\n${e.stack}`),{error:e}),process.exit(1)}finally{j((e=>s.createLogger(t.logLevel).info(e)))}})),$.command("changelog [oldAssetPath] [newAssetPath]","generate changelog between two assets files/folders").option("--oldAssetsPath <oldAssetsPath>","[string] path to the old assets zip file or folder").option("--newAssetsPath <newAssetsPath>","[string] path to the new assets zip file or folder").option("--destination <destination>","[string] destination folder for the changelog (default: .)").option("--filename <filename>","[string] filename for the changelog (default: CHANGELOG.html)").action((async(e,t,o)=>{D(o);const{oldAssetsPath:n=e,newAssetsPath:i=t,destination:r=".",filename:a="CHANGELOG.html",logLevel:l}=o,c=process.cwd();n&&i||(s.createLogger(l).error(s.colors.red("error during changelog generation: oldAssetPath and newAssetPath are required")),process.exit(1));const d=m.resolve(c,n),p=m.resolve(c,i),f=m.resolve(c,r),h=new T({oldAssetsPath:d,newAssetsPath:p,destinationPath:f,changelogFilename:a});await h.generateChangelog()})),$.command("generate-icon-font [source] [destination]","generate icon font from SVG files").option("--source <source>","[string] path to the source directory with SVG files").option("--destination <destination>","[string] path to the destination directory to save the generated font files").option("--font-name, -n <fontName>","[string] name of the font to generate (default: 'icon-font')").action((async(e,t,o)=>{D(o);const{source:n=e,destination:i=t,fontName:r="icon-font"}=o,a=process.cwd(),l=m.resolve(a,n),c=m.resolve(a,i),d=new F({sourceDir:l,outputDir:c,fontName:r}),p=s.createLogger(o.logLevel);p.info(`Generating icon font from SVG files in ${s.colors.dim(l)}`),await d.generate(),p.info(`Icon font generated and saved to ${s.colors.dim(c)}`)})),$.command("optimize [root]","pre-bundle dependencies").option("--force","[boolean] force the optimizer to ignore the cache and re-bundle").action((async(e,t)=>{D(t);try{const o=await i.loadConfigFromFile({mode:t.mode||"production",command:"build"},t.config,e,t.logLevel);let n=await r.getViteConfig();if(o){const{plugins:e,...t}=o.config;n=i.mergeConfig(n,t)}const s=await i.resolveConfig(i.mergeConfig(n,{root:e,base:t.base,configFile:t.config,logLevel:t.logLevel,mode:t.mode}),"serve");await i.optimizeDeps(s,t.force,!0)}catch(e){s.createLogger(t.logLevel).error(s.colors.red(`error when optimizing deps:\n${e.stack}`),{error:e}),process.exit(1)}})),$.command("preview [root]","locally preview production build").option("--host [host]","[string] specify hostname").option("--port <port>","[number] specify port").option("--strictPort","[boolean] exit if specified port is already in use").option("--https","[boolean] use TLS + HTTP/2").option("--open [path]","[boolean | string] open browser on startup").option("--outDir <dir>","[string] output directory (default: dist)").action((async(e,t)=>{D(t);try{const o=await i.loadConfigFromFile({mode:t.mode||"production",command:"build"},t.config,e,t.logLevel);let n=await r.getViteConfig();if(o){const{plugins:e,...t}=o.config;n=i.mergeConfig(n,t)}(await i.preview(i.mergeConfig(n,{root:e,base:t.base,configFile:t.config,logLevel:t.logLevel,mode:t.mode,build:{outDir:t.outDir},preview:{port:t.port,strictPort:t.strictPort,host:t.host,https:t.https,open:t.open}}))).printUrls()}catch(e){s.createLogger(t.logLevel).error(s.colors.red(`error when starting preview server:\n${e.stack}`),{error:e}),process.exit(1)}finally{j((e=>s.createLogger(t.logLevel).info(e)))}})),$.help(),$.version(r.VERSION),$.parse(),exports.stopProfiler=j;
|
|
2
2
|
//# sourceMappingURL=cli.js.map
|