xpine 0.0.44 → 0.0.46
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 +387 -55
- package/TODO +2 -0
- package/dist/index.js +19 -14
- package/dist/src/express.d.ts.map +1 -1
- package/dist/src/runDevServer.d.ts.map +1 -1
- package/dist/src/scripts/xpine-build.js +2 -2
- package/dist/src/scripts/xpine-dev.js +5 -3
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/types.ts +2 -1
package/README.md
CHANGED
|
@@ -1,64 +1,367 @@
|
|
|
1
1
|
# XPine - Alpine.js + JSX + Tailwind framework
|
|
2
2
|
|
|
3
|
-
Combines JSX with Alpine.js for a simpler, easier development experience.
|
|
3
|
+
Combines JSX with Alpine.js for a simpler, easier development experience. Includes a static site generator.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`npm install xpine`
|
|
8
|
+
|
|
9
|
+
### Routing, page setup, and using Alpine.js
|
|
10
|
+
|
|
11
|
+
XPine uses page based routing. Render an HTML page using JSX components, for example the path `/src/page/about.tsx` will route to `/about` and `/src/page/index.tsx` will route to `/`
|
|
8
12
|
|
|
9
13
|
```
|
|
10
|
-
|
|
14
|
+
import { WrapperProps } from 'xpine/dist/types';
|
|
15
|
+
import Base from '../components/Base';
|
|
16
|
+
|
|
17
|
+
export const config = {
|
|
18
|
+
data() {
|
|
19
|
+
return {
|
|
20
|
+
title: 'Home page',
|
|
21
|
+
description: 'The description',
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
wrapper({ req, children, data, routePath }: WrapperProps) {
|
|
25
|
+
return (
|
|
26
|
+
<Base
|
|
27
|
+
title={data?.title || 'My awesome website'}
|
|
28
|
+
description={data?.description}
|
|
29
|
+
req={req}
|
|
30
|
+
>
|
|
31
|
+
<h1>Home page wrapper</h1>
|
|
32
|
+
{children}
|
|
33
|
+
</Base>
|
|
34
|
+
)
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function Home() {
|
|
39
|
+
return (
|
|
40
|
+
<div x-data="HomePageData" x-on:click="logMessage">
|
|
41
|
+
Hello world
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<script />
|
|
47
|
+
|
|
48
|
+
export function HomePageData() {
|
|
49
|
+
return {
|
|
50
|
+
logMessage() {
|
|
51
|
+
console.log('Hello world');
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
11
55
|
```
|
|
12
56
|
|
|
13
|
-
|
|
57
|
+
- src/components/Base.tsx
|
|
14
58
|
|
|
15
|
-
|
|
59
|
+
```
|
|
60
|
+
import { JsxElement } from 'typescript';
|
|
61
|
+
import { ServerRequest } from 'xpine/dist/types';
|
|
16
62
|
|
|
17
|
-
|
|
63
|
+
type BaseProps = {
|
|
64
|
+
head?: JsxElement;
|
|
65
|
+
title: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
req?: ServerRequest;
|
|
68
|
+
children?: JsxElement;
|
|
69
|
+
}
|
|
18
70
|
|
|
19
|
-
|
|
71
|
+
export default async function Base({
|
|
72
|
+
head,
|
|
73
|
+
title,
|
|
74
|
+
description,
|
|
75
|
+
children,
|
|
76
|
+
}: BaseProps) {
|
|
77
|
+
return (
|
|
78
|
+
<html lang="en">
|
|
79
|
+
<head>
|
|
80
|
+
<meta charset="UTF-8" />
|
|
81
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
82
|
+
<meta name="robots" content="index,follow" />
|
|
83
|
+
<meta name="description" content={description || `${title} page`} />
|
|
84
|
+
<title>{title || 'My Website'}</title>
|
|
85
|
+
<link rel="stylesheet" href="/styles/global.css" />
|
|
86
|
+
<script defer src="/scripts/app.js"></script>
|
|
87
|
+
{head}
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<div id="xpine-root">
|
|
91
|
+
{children}
|
|
92
|
+
</div>
|
|
93
|
+
</body>
|
|
94
|
+
</html>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
20
98
|
|
|
21
|
-
|
|
99
|
+
### Client side Javascript and CSS files
|
|
22
100
|
|
|
23
|
-
|
|
101
|
+
In the above code, we have a `/styles/global.css` file and a `/scripts/app.js` file import.
|
|
24
102
|
|
|
25
|
-
|
|
103
|
+
#### CSS
|
|
26
104
|
|
|
27
|
-
|
|
105
|
+
Create a file called `/src/public/styles/global.css` and import Tailwind:
|
|
28
106
|
|
|
29
|
-
|
|
107
|
+
```
|
|
108
|
+
@import "tailwindcss";
|
|
109
|
+
```
|
|
30
110
|
|
|
31
|
-
|
|
111
|
+
Note you must install tailwindcss yourself: `npm install tailwindcss`
|
|
112
|
+
|
|
113
|
+
#### Javascript
|
|
114
|
+
|
|
115
|
+
Create a file called `/src/public/scripts/index.ts`:
|
|
32
116
|
|
|
33
117
|
```
|
|
34
|
-
import
|
|
118
|
+
import Alpine from 'alpinejs';
|
|
35
119
|
|
|
36
|
-
|
|
120
|
+
Alpine.start();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Note you must install alpinejs yourself: `npm install alpinejs`
|
|
124
|
+
|
|
125
|
+
### Dynamic routing
|
|
126
|
+
|
|
127
|
+
Create dynamic routes with paths similar to this `/src/pages/[pathA]/[pathB]`
|
|
37
128
|
|
|
129
|
+
### Express API endpoints
|
|
130
|
+
|
|
131
|
+
Create regular Express routes by using .ts file extensions in the `/src/pages` folder. Specify the HTTP method by naming the file something like `/src/pages/api/my-endpoint.POST.ts`
|
|
38
132
|
```
|
|
133
|
+
import { PageProps } from 'xpine/dist/types';
|
|
39
134
|
|
|
40
|
-
|
|
135
|
+
export default async function myEndpoint({ res }: PageProps) {
|
|
136
|
+
res.status(200).json({
|
|
137
|
+
message: 'Hello World!',
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
```
|
|
41
141
|
|
|
42
|
-
|
|
142
|
+
### Catch all routes
|
|
143
|
+
You can create catch all routes by naming the file \_all\_.(jsx|tsx|js|ts). You can make static catch all route pages by using the param `0` in the staticPaths config function:
|
|
144
|
+
```
|
|
145
|
+
export const config = {
|
|
146
|
+
staticPaths() {
|
|
147
|
+
return [
|
|
148
|
+
{
|
|
149
|
+
0: 'hello/world',
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
```
|
|
43
155
|
|
|
44
|
-
|
|
156
|
+
You can get the route param in your function with req.params[0], such as how express handles catch all routes.
|
|
45
157
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
158
|
+
### Route specific middleware
|
|
159
|
+
|
|
160
|
+
If you need route specific middleware, e.g. for file uploads, you can specify a `routeMiddleware` function in a config variable in the endpoint file:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
export const config = {
|
|
164
|
+
routeMiddleware(req, res, next) {
|
|
165
|
+
console.log('route middleware');
|
|
166
|
+
next();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Static Site Generation
|
|
172
|
+
|
|
173
|
+
Generate path specific static pages by specifying in the config of either the page's file, such as `/src/pages/about.tsx` with a config export:
|
|
174
|
+
```
|
|
175
|
+
export const config = {
|
|
176
|
+
staticPaths: true,
|
|
177
|
+
data() {
|
|
178
|
+
return {
|
|
179
|
+
title: 'My title'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
or a `+config.ts` file.
|
|
186
|
+
|
|
187
|
+
### Configs
|
|
188
|
+
|
|
189
|
+
Configs can be nested. Create a +config.ts file in a directory and all subfolders will inherit that config unless overridden by their own +config.ts files. Want to apply static paths to an entire folder except for a single folder? In that folder you can create a `+config.ts` file like this:
|
|
190
|
+
```
|
|
191
|
+
export default {
|
|
192
|
+
staticPaths: false,
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Dynamic Static Pages
|
|
197
|
+
|
|
198
|
+
You can also create dynamic static pages by using a function in the staticPaths folder. For example, a directory named `/src/[pathA]/[pathB]/[pathC]/[pathD].tsx` might have a configuration file like this:
|
|
199
|
+
```
|
|
200
|
+
import { ServerRequest } from 'xpine/dist/types';
|
|
201
|
+
import axios from 'axios';
|
|
202
|
+
|
|
203
|
+
export const config = {
|
|
204
|
+
staticPaths() {
|
|
205
|
+
return [
|
|
206
|
+
{
|
|
207
|
+
pathA: 'my-path-a2',
|
|
208
|
+
pathB: 'my-path-b2',
|
|
209
|
+
pathC: 'my-path-c2',
|
|
210
|
+
pathD: '2'
|
|
58
211
|
}
|
|
59
|
-
|
|
60
|
-
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
async data(req: ServerRequest) {
|
|
215
|
+
const url = `https://jsonplaceholder.typicode.com/posts/${req.params.pathD}`;
|
|
216
|
+
try {
|
|
217
|
+
const { data } = await axios.get(url);
|
|
218
|
+
return {
|
|
219
|
+
...data,
|
|
220
|
+
...req.params,
|
|
221
|
+
};
|
|
222
|
+
} catch (err) {
|
|
223
|
+
console.error('could not fetch', url);
|
|
224
|
+
return {
|
|
225
|
+
...req.params,
|
|
226
|
+
data: {},
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Context
|
|
234
|
+
|
|
235
|
+
Create app context, useful for things like Navbars. In `/src/context.tsx`:
|
|
236
|
+
```
|
|
237
|
+
import { createContext } from 'xpine';
|
|
238
|
+
|
|
239
|
+
export function NavbarContext() {
|
|
240
|
+
const navbar = createContext([]);
|
|
241
|
+
return {
|
|
242
|
+
navbar,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
then in a page, say `/src/pages/about.tsx`, you can add to the NavbarContext like this:
|
|
248
|
+
```
|
|
249
|
+
export function xpineOnLoad() {
|
|
250
|
+
context.addToArray('navbar', 'My awesome context 1', 2);
|
|
251
|
+
context.addToArray('navbar', 'My awesome context 2', new Date('January 11, 2024'));
|
|
252
|
+
context.addToArray('navbar', 'My awesome context 3', new Date('January 10, 2024'));
|
|
253
|
+
context.addToArray('navbar', 'My awesome context 4', new Date('January 30, 2024'));
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Context is sorted by array position then by date. You can then use context in your component like this:
|
|
258
|
+
```
|
|
259
|
+
import { context } from 'xpine';
|
|
260
|
+
|
|
261
|
+
export default function Navbar() {
|
|
262
|
+
const navbar = context.get('navbar');
|
|
263
|
+
return (
|
|
264
|
+
<div>
|
|
265
|
+
{navbar.map(item => {
|
|
266
|
+
return <div>{item}</div>
|
|
267
|
+
})}
|
|
268
|
+
</div>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
```
|
|
61
272
|
|
|
273
|
+
### Server set up
|
|
274
|
+
|
|
275
|
+
- src/server/app.ts
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
import express from 'express';
|
|
279
|
+
import cookieParser from 'cookie-parser';
|
|
280
|
+
import logger from 'morgan';
|
|
281
|
+
import http from 'http';
|
|
282
|
+
import { createXPineRouter, setupEnv } from 'xpine';
|
|
283
|
+
|
|
284
|
+
await setupEnv();
|
|
285
|
+
|
|
286
|
+
export default async function startServer() {
|
|
287
|
+
const port = process.env.PORT || 8080;
|
|
288
|
+
const app = express();
|
|
289
|
+
app.set('view engine', 'html');
|
|
290
|
+
|
|
291
|
+
app.enable('trust proxy');
|
|
292
|
+
app.use(logger('dev'));
|
|
293
|
+
app.use(express.json());
|
|
294
|
+
app.use(express.urlencoded({ extended: true, }));
|
|
295
|
+
app.use(cookieParser());
|
|
296
|
+
|
|
297
|
+
await createXPineRouter(app);
|
|
298
|
+
|
|
299
|
+
app.set('port', port);
|
|
300
|
+
const server = http.createServer(app);
|
|
301
|
+
server.listen(port, () => {
|
|
302
|
+
console.info(`Server listening on port ${port}`);
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
app,
|
|
306
|
+
server,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Add a directory in `/src/server` called `run`, and create two files: `/src/server/dev.ts` and `/src/server/prod.ts`.
|
|
312
|
+
|
|
313
|
+
`dev.ts` should look like this:
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
import { runDevServer } from 'xpine';
|
|
317
|
+
|
|
318
|
+
runDevServer();
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
and can be run with an npm command like this in your package.json scripts: `"dev": "PORT=8888 LOCALHOST=1 xpine-dev"`.
|
|
322
|
+
|
|
323
|
+
The `prod.ts` should look like this:
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
import startServer from '../app';
|
|
327
|
+
|
|
328
|
+
await startServer();
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
and can be run with an npm command in your package.json scripts like this, after the app has been built with `xpine-build`:
|
|
332
|
+
|
|
333
|
+
`"start": "PORT=8888 node ./dist/server/run/prod.js"`
|
|
334
|
+
|
|
335
|
+
### xpine.config.mjs file
|
|
336
|
+
|
|
337
|
+
Add an xpine.config.mjs file to your root directory. This is used primarily for configuring/changing file paths. Configs can be imported with `import { config } from "xpine"`.
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
export default {}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
These are the following default paths:
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
rootDir: process.cwd
|
|
347
|
+
srcDir: rootDir + ./src
|
|
348
|
+
distDir: rootDir + ./dist
|
|
349
|
+
packageJsonPath: rootDir + ./package.json
|
|
350
|
+
distPublicDir: distDir + ./public
|
|
351
|
+
distPublicScriptsDir: distPublicDir + ./scripts
|
|
352
|
+
distTempFolder: distDir + ./temp
|
|
353
|
+
clientJSBundlePath: distPublicScriptsDir + ./app.js
|
|
354
|
+
alpineDataPath: distTempFolder + ./alpine-data.ts
|
|
355
|
+
serverDistDir: distDir + ./server
|
|
356
|
+
serverDistAppPath: serverDistDir + ./app.js
|
|
357
|
+
pagesDir: srcDir + ./pages
|
|
358
|
+
distPagesDir: distDir + ./pages
|
|
359
|
+
publicDir: srcDir + ./public
|
|
360
|
+
serverDir: srcDir + ./server
|
|
361
|
+
runDir: serverDir + ./run
|
|
362
|
+
serverAppPath: serverDir + ./app.ts
|
|
363
|
+
globalCSSFile: publicDir + ./styles/global.css
|
|
364
|
+
```
|
|
62
365
|
|
|
63
366
|
### SPA interactivity
|
|
64
367
|
|
|
@@ -69,29 +372,58 @@ setupEnv also supports AWS secrets manager. Simply add SECRETS_NAME=your_aws_sec
|
|
|
69
372
|
- data-spa-crossorigin="true"
|
|
70
373
|
- enables cross-origin spa navigation requests (untested)
|
|
71
374
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
375
|
+
## API
|
|
376
|
+
|
|
377
|
+
### Build command
|
|
378
|
+
|
|
379
|
+
`xpine-build`
|
|
380
|
+
|
|
381
|
+
### Dev server command
|
|
382
|
+
|
|
383
|
+
`xpine-dev`
|
|
384
|
+
|
|
385
|
+
### Auth
|
|
386
|
+
|
|
387
|
+
`import { signUser, verifyUser } from 'xpine';`
|
|
388
|
+
|
|
389
|
+
### Config
|
|
390
|
+
|
|
391
|
+
`import { config } from 'xpine';`
|
|
392
|
+
|
|
393
|
+
### Env
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
import { setupEnv } from 'xpine';
|
|
397
|
+
|
|
398
|
+
await setupEnv();
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
setupEnv also supports AWS secrets manager. Simply add SECRETS_NAME=your_aws_secret_name to your .env.{stage} file
|
|
403
|
+
|
|
404
|
+
### Custom events
|
|
405
|
+
- spa-update-page-content
|
|
406
|
+
- sent when the page content has update
|
|
407
|
+
- spa-update-page-url
|
|
408
|
+
- sent when the page URL has update
|
|
409
|
+
- spa-link-click
|
|
410
|
+
- sent when link initially gets clicked and when link is done updating content
|
|
411
|
+
- the "state" value in the event detail will be "start" or "end"
|
|
412
|
+
- breakpoint-change
|
|
413
|
+
- sent when a breakpoint is changed via your Tailwind css file's breakpoints in the @theme directive, e.g.
|
|
414
|
+
```
|
|
415
|
+
@theme {
|
|
416
|
+
--breakpoint-xl: 1184px;
|
|
417
|
+
--breakpoint-sm: 640px;
|
|
418
|
+
--breakpoint-md: 768px;
|
|
419
|
+
--breakpoint-lg: 1024px;
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
- this will send a custom event with the detail being the breakpoint, such as `{ breakpoint: 'xl' }`
|
|
423
|
+
- note this will only send an event based on the values of the breakpoints in your @theme configuration
|
|
92
424
|
|
|
93
|
-
|
|
425
|
+
### Custom scripts for certain pages
|
|
94
426
|
|
|
95
427
|
1. Add script to src/public/scripts/pages/your_script.ts
|
|
96
|
-
2. Import script into page HTML (e.g.
|
|
428
|
+
2. Import script into page HTML (e.g. `<script src="/scripts/pages/your_script.ts">`)
|
|
97
429
|
3. To unload event listeners, use `window.addEventListener('spa-update-page-url', () => { remove event listeners here})` in the code
|
package/TODO
CHANGED
package/dist/index.js
CHANGED
|
@@ -512,7 +512,7 @@ function getAllBuiltRoutes() {
|
|
|
512
512
|
async function createRouteFunction(route, configFiles) {
|
|
513
513
|
const isJSX = route.originalRoute.endsWith(".tsx") || route.originalRoute.endsWith(".jsx");
|
|
514
514
|
const slugRoute = route.route.replace(/[ ]/g, "");
|
|
515
|
-
const foundMethod = methods.find((method) => slugRoute.endsWith(`.${method}`));
|
|
515
|
+
const foundMethod = methods.find((method) => slugRoute.toUpperCase().endsWith(`.${method.toUpperCase()}`));
|
|
516
516
|
const isDynamicRoute = slugRoute.match(regex_default.isDynamicRoute);
|
|
517
517
|
let formattedRouteItem = slugRoute;
|
|
518
518
|
if (foundMethod) formattedRouteItem = formattedRouteItem.split(".").shift();
|
|
@@ -531,14 +531,12 @@ async function createRouteFunction(route, configFiles) {
|
|
|
531
531
|
}
|
|
532
532
|
let config2 = {};
|
|
533
533
|
const configFilePaths = getConfigFiles(route.originalRoute, configFiles);
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
config2
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
};
|
|
541
|
-
}
|
|
534
|
+
config2 = configFilePaths && await getCompleteConfig(configFilePaths, Date.now());
|
|
535
|
+
if (componentImport?.config) {
|
|
536
|
+
config2 = {
|
|
537
|
+
...config2,
|
|
538
|
+
...componentImport.config
|
|
539
|
+
};
|
|
542
540
|
}
|
|
543
541
|
async function routeFn(req, res) {
|
|
544
542
|
const urlPath = req?.route?.path || "/";
|
|
@@ -588,6 +586,7 @@ async function createRouteFunction(route, configFiles) {
|
|
|
588
586
|
formattedRouteItem,
|
|
589
587
|
foundMethod,
|
|
590
588
|
route,
|
|
589
|
+
config: config2,
|
|
591
590
|
routeFn
|
|
592
591
|
};
|
|
593
592
|
}
|
|
@@ -599,8 +598,12 @@ async function createRouter() {
|
|
|
599
598
|
await triggerXPineOnLoad();
|
|
600
599
|
for (const route of routeMap) {
|
|
601
600
|
try {
|
|
602
|
-
const { formattedRouteItem, foundMethod, routeFn } = await createRouteFunction(route, configFiles);
|
|
603
|
-
|
|
601
|
+
const { formattedRouteItem, foundMethod, config: config2, routeFn } = await createRouteFunction(route, configFiles);
|
|
602
|
+
if (config2?.routeMiddleware) {
|
|
603
|
+
router[foundMethod || "get"](formattedRouteItem, config2.routeMiddleware, routeFn);
|
|
604
|
+
} else {
|
|
605
|
+
router[foundMethod || "get"](formattedRouteItem, routeFn);
|
|
606
|
+
}
|
|
604
607
|
routeResults.push({
|
|
605
608
|
formattedRouteItem,
|
|
606
609
|
foundMethod,
|
|
@@ -787,7 +790,7 @@ async function buildAppFiles(files, isDev2) {
|
|
|
787
790
|
const fileName = file.split("/").at(-1).split(".").shift();
|
|
788
791
|
return fileName === "+config";
|
|
789
792
|
});
|
|
790
|
-
const backendFiles = files.filter((file) => extensions
|
|
793
|
+
const backendFiles = files.filter((file) => extensions?.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
|
|
791
794
|
fs7.ensureDirSync(config.distDir);
|
|
792
795
|
await build({
|
|
793
796
|
entryPoints: backendFiles,
|
|
@@ -918,7 +921,7 @@ async function buildPublicFolderSymlinks() {
|
|
|
918
921
|
async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
|
|
919
922
|
const files = globSync3(pathName + "/**/*" + (type === "css" ? ".css" : ""));
|
|
920
923
|
const fileSizes = files.map((file) => {
|
|
921
|
-
if (!validExtensions
|
|
924
|
+
if (!validExtensions?.find((ext) => file.endsWith(ext))) return false;
|
|
922
925
|
return {
|
|
923
926
|
file,
|
|
924
927
|
size: fs7.statSync(file).size / (1024 * 1e3)
|
|
@@ -1108,7 +1111,9 @@ async function runDevServer() {
|
|
|
1108
1111
|
ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path8.join(config.serverDir, "./prisma"))
|
|
1109
1112
|
});
|
|
1110
1113
|
watcher.on("all", async (event, path9) => {
|
|
1111
|
-
const
|
|
1114
|
+
const isRegularExpressRoute = path9.startsWith(config.pagesDir) && (path9.endsWith(".ts") || path9.endsWith(".js"));
|
|
1115
|
+
const isServerDir = path9.startsWith(config.serverDir) && !path9.startsWith(config.runDir);
|
|
1116
|
+
const shouldReloadServer = isServerDir || isRegularExpressRoute || ["add", "unlink"].includes(event) && path9.startsWith(config.pagesDir);
|
|
1112
1117
|
if (shouldReloadServer) {
|
|
1113
1118
|
await asyncServerClose(appServer.server);
|
|
1114
1119
|
rebuildEmitter.emit("rebuild-server");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAgB,OAAO,EAAqB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAgB,OAAO,EAAqB,MAAM,SAAS,CAAC;AAqJ5E,wBAAsB,YAAY;;;GAkCjC;AAiBD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,iBAyB1F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,KAAK,CAanG;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,UAQzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runDevServer.d.ts","sourceRoot":"","sources":["../../src/runDevServer.ts"],"names":[],"mappings":"AAYA,wBAAsB,YAAY,
|
|
1
|
+
{"version":3,"file":"runDevServer.d.ts","sourceRoot":"","sources":["../../src/runDevServer.ts"],"names":[],"mappings":"AAYA,wBAAsB,YAAY,kBA+DjC"}
|
|
@@ -583,7 +583,7 @@ async function buildAppFiles(files, isDev3) {
|
|
|
583
583
|
const fileName = file.split("/").at(-1).split(".").shift();
|
|
584
584
|
return fileName === "+config";
|
|
585
585
|
});
|
|
586
|
-
const backendFiles = files.filter((file) => extensions
|
|
586
|
+
const backendFiles = files.filter((file) => extensions?.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
|
|
587
587
|
fs7.ensureDirSync(config.distDir);
|
|
588
588
|
await build({
|
|
589
589
|
entryPoints: backendFiles,
|
|
@@ -714,7 +714,7 @@ async function buildPublicFolderSymlinks() {
|
|
|
714
714
|
async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
|
|
715
715
|
const files = globSync3(pathName + "/**/*" + (type === "css" ? ".css" : ""));
|
|
716
716
|
const fileSizes = files.map((file) => {
|
|
717
|
-
if (!validExtensions
|
|
717
|
+
if (!validExtensions?.find((ext) => file.endsWith(ext))) return false;
|
|
718
718
|
return {
|
|
719
719
|
file,
|
|
720
720
|
size: fs7.statSync(file).size / (1024 * 1e3)
|
|
@@ -590,7 +590,7 @@ async function buildAppFiles(files, isDev2) {
|
|
|
590
590
|
const fileName = file.split("/").at(-1).split(".").shift();
|
|
591
591
|
return fileName === "+config";
|
|
592
592
|
});
|
|
593
|
-
const backendFiles = files.filter((file) => extensions
|
|
593
|
+
const backendFiles = files.filter((file) => extensions?.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
|
|
594
594
|
fs7.ensureDirSync(config.distDir);
|
|
595
595
|
await build({
|
|
596
596
|
entryPoints: backendFiles,
|
|
@@ -721,7 +721,7 @@ async function buildPublicFolderSymlinks() {
|
|
|
721
721
|
async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
|
|
722
722
|
const files = globSync3(pathName + "/**/*" + (type === "css" ? ".css" : ""));
|
|
723
723
|
const fileSizes = files.map((file) => {
|
|
724
|
-
if (!validExtensions
|
|
724
|
+
if (!validExtensions?.find((ext) => file.endsWith(ext))) return false;
|
|
725
725
|
return {
|
|
726
726
|
file,
|
|
727
727
|
size: fs7.statSync(file).size / (1024 * 1e3)
|
|
@@ -911,7 +911,9 @@ async function runDevServer() {
|
|
|
911
911
|
ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path7.join(config.serverDir, "./prisma"))
|
|
912
912
|
});
|
|
913
913
|
watcher.on("all", async (event, path8) => {
|
|
914
|
-
const
|
|
914
|
+
const isRegularExpressRoute = path8.startsWith(config.pagesDir) && (path8.endsWith(".ts") || path8.endsWith(".js"));
|
|
915
|
+
const isServerDir = path8.startsWith(config.serverDir) && !path8.startsWith(config.runDir);
|
|
916
|
+
const shouldReloadServer = isServerDir || isRegularExpressRoute || ["add", "unlink"].includes(event) && path8.startsWith(config.pagesDir);
|
|
915
917
|
if (shouldReloadServer) {
|
|
916
918
|
await asyncServerClose(appServer.server);
|
|
917
919
|
rebuildEmitter.emit("rebuild-server");
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
export type XPineConfig = {
|
|
4
4
|
[key: string]: any;
|
|
@@ -24,6 +24,7 @@ export type ConfigFile = {
|
|
|
24
24
|
}[]>);
|
|
25
25
|
wrapper?: (props: WrapperProps) => Promise<any>;
|
|
26
26
|
data?: (req: ServerRequest) => Promise<any>;
|
|
27
|
+
routeMiddleware?: (req: ServerRequest, res: Response, next: NextFunction) => void;
|
|
27
28
|
};
|
|
28
29
|
export type PageProps = {
|
|
29
30
|
req: ServerRequest;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5C,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACnF,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,aAAa,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC;CACvB,CAAA"}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
|
|
4
4
|
export type XPineConfig = {
|
|
@@ -27,6 +27,7 @@ export type ConfigFile = {
|
|
|
27
27
|
staticPaths?: boolean | (() => Promise<{ [key: string]: string }[]>);
|
|
28
28
|
wrapper?: (props: WrapperProps) => Promise<any>;
|
|
29
29
|
data?: (req: ServerRequest) => Promise<any>;
|
|
30
|
+
routeMiddleware?: (req: ServerRequest, res: Response, next: NextFunction) => void;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export type PageProps = {
|