xpine 0.0.44 → 0.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,64 +1,308 @@
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
- ## Get started
5
+ ### Install
6
6
 
7
- Add an xpine.config.mjs file to your root directory
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
- export default {}
14
+ import { WrapperProps } from 'xpine/dist/types';
15
+ import Base from '../components/Base';
16
+ import Navbar from '../components/Navbar';
17
+
18
+ export const config = {
19
+ data() {
20
+ return {
21
+ title: 'Home page',
22
+ description: 'The description',
23
+ }
24
+ },
25
+ wrapper({ req, children, data, routePath }: WrapperProps) {
26
+ return (
27
+ <Base
28
+ title={data?.title || 'My awesome website'}
29
+ description={data?.description}
30
+ req={req}
31
+ >
32
+ <h1>Home page wrapper</h1>
33
+ {children}
34
+ </Base>
35
+ )
36
+ },
37
+ }
38
+
39
+ export default function Home() {
40
+ return (
41
+ <div x-data="HomePageData" x-on:click="logMessage">
42
+ Hello world
43
+ </div>
44
+ );
45
+ }
46
+
47
+ <script />
48
+
49
+ export function HomePageData() {
50
+ return {
51
+ logMessage() {
52
+ console.log('Hello world');
53
+ }
54
+ };
55
+ }
11
56
  ```
12
57
 
13
- ## API
58
+ - src/components/Base.tsx
14
59
 
15
- ### Build
60
+ ```
61
+ import { JsxElement } from 'typescript';
62
+ import { ServerRequest } from 'xpine/dist/types';
16
63
 
17
- `xpine-build`
64
+ type BaseProps = {
65
+ head?: JsxElement;
66
+ title: string;
67
+ description?: string;
68
+ req?: ServerRequest;
69
+ children?: JsxElement;
70
+ }
18
71
 
19
- ## Auth
72
+ export default async function Base({
73
+ head,
74
+ title,
75
+ description,
76
+ children,
77
+ }: BaseProps) {
78
+ return (
79
+ <html lang="en">
80
+ <head>
81
+ <meta charset="UTF-8" />
82
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
83
+ <meta name="robots" content="index,follow" />
84
+ <meta name="description" content={description || `${title} page`} />
85
+ <title>{title || 'My Website'}</title>
86
+ <link rel="stylesheet" href="/styles/global.css" />
87
+ <script defer src="/scripts/app.js"></script>
88
+ {head}
89
+ </head>
90
+ <body>
91
+ <div id="xpine-root">
92
+ {children}
93
+ </div>
94
+ </body>
95
+ </html>
96
+ );
97
+ }
98
+ ```
20
99
 
21
- `import { signUser, verifyUser } from 'xpine';`
100
+ ### Client side Javascript and CSS files
22
101
 
23
- ## Dev server
102
+ In the above code, we have a `/styles/global.css` file and a `/scripts/app.js` file import.
24
103
 
25
- `xpine-dev`
104
+ #### CSS
26
105
 
27
- ## Config
106
+ Create a file called `/src/public/styles/global.css` and import Tailwind:
28
107
 
29
- `import { config } from 'xpine';`
108
+ ```
109
+ @import "tailwindcss";
110
+ ```
30
111
 
31
- ## Env
112
+ Note you must install tailwindcss yourself: `npm install tailwindcss`
113
+
114
+ #### Javascript
115
+
116
+ Create a file called `/src/public/scripts/index.ts`:
32
117
 
33
118
  ```
34
- import { setupEnv } from 'xpine';
119
+ import Alpine from 'alpinejs';
35
120
 
36
- await setupEnv();
121
+ Alpine.start();
122
+ ```
123
+
124
+ Note you must install alpinejs yourself: `npm install alpinejs`
125
+
126
+ ### Dynamic routing
127
+
128
+ Create dynamic routes with paths similar to this `/src/pages/[pathA]/[pathB]`
129
+
130
+ ### Express API endpoints
37
131
 
132
+ 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
133
  ```
134
+ import { PageProps } from 'xpine/dist/types';
39
135
 
40
- setupEnv also supports AWS secrets manager. Simply add SECRETS_NAME=your_aws_secret_name to your .env.{stage} file
136
+ export default async function myEndpoint({ res }: PageProps) {
137
+ res.status(200).json({
138
+ message: 'Hello World!',
139
+ });
140
+ }
141
+ ```
41
142
 
42
- ## Router
143
+ ### Catch all routes
144
+ 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:
145
+ ```
146
+ export const config = {
147
+ staticPaths() {
148
+ return [
149
+ {
150
+ 0: 'hello/world',
151
+ }
152
+ ]
153
+ },
154
+ }
155
+ ```
43
156
 
44
- `import { createXPineRouter } from 'xpine';`
157
+ You can get the route param in your function with req.params[0], such as how express handles catch all routes.
45
158
 
46
- - Catch all routes
47
- - You can create catch all routes by naming the file _all_.(jsx|tsx|js|ts)
48
- - You can make static catch all route pages by using the param `0` in the staticPaths config function:
49
- ```
50
- export const config = {
51
- staticPaths() {
52
- return [
53
- {
54
- 0: 'hello/world',
55
- }
56
- ]
57
- },
159
+ ### Static Site Generation
160
+
161
+ 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:
162
+ ```
163
+ export const config = {
164
+ staticPaths: true,
165
+ data() {
166
+ return {
167
+ title: 'My title'
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ or a `+config.ts` file.
174
+
175
+ ### Configs
176
+
177
+ 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:
178
+ ```
179
+ export default {
180
+ staticPaths: false,
181
+ }
182
+ ```
183
+
184
+ ### Dynamic Static Pages
185
+
186
+ 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:
187
+ ```
188
+ import { ServerRequest } from 'xpine/dist/types';
189
+ import axios from 'axios';
190
+
191
+ export const config = {
192
+ staticPaths() {
193
+ return [
194
+ {
195
+ pathA: 'my-path-a2',
196
+ pathB: 'my-path-b2',
197
+ pathC: 'my-path-c2',
198
+ pathD: '2'
58
199
  }
59
- ```
60
- - You can get the route param in your function with req.params[0], such as how express handles catch all routes
200
+ ]
201
+ },
202
+ async data(req: ServerRequest) {
203
+ const url = `https://jsonplaceholder.typicode.com/posts/${req.params.pathD}`;
204
+ try {
205
+ const { data } = await axios.get(url);
206
+ return {
207
+ ...data,
208
+ ...req.params,
209
+ };
210
+ } catch (err) {
211
+ console.error('could not fetch', url);
212
+ return {
213
+ ...req.params,
214
+ data: {},
215
+ }
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ ### Context
222
+
223
+ Create app context, useful for things like Navbars. In `/src/context.tsx`:
224
+ ```
225
+ import { createContext } from 'xpine';
226
+
227
+ export function NavbarContext() {
228
+ const navbar = createContext([]);
229
+ return {
230
+ navbar,
231
+ }
232
+ }
233
+ ```
61
234
 
235
+ then in a page, say `/src/pages/about.tsx`, you can add to the NavbarContext like this:
236
+ ```
237
+ export function xpineOnLoad() {
238
+ context.addToArray('navbar', 'My awesome context 1', 2);
239
+ context.addToArray('navbar', 'My awesome context 2', new Date('January 11, 2024'));
240
+ context.addToArray('navbar', 'My awesome context 3', new Date('January 10, 2024'));
241
+ context.addToArray('navbar', 'My awesome context 4', new Date('January 30, 2024'));
242
+ }
243
+ ```
244
+
245
+ Context is sorted by array position then by date. You can then use context in your component like this:
246
+ ```
247
+ import { context } from 'xpine';
248
+
249
+ export default function Navbar() {
250
+ const navbar = context.get('navbar');
251
+ return (
252
+ <div>
253
+ {navbar.map(item => {
254
+ return <div>{item}</div>
255
+ })}
256
+ </div>
257
+ );
258
+ }
259
+ ```
260
+
261
+ ### Server set up
262
+
263
+ - src/server/app.ts
264
+
265
+ ```
266
+ import express from 'express';
267
+ import cookieParser from 'cookie-parser';
268
+ import logger from 'morgan';
269
+ import http from 'http';
270
+ import { createXPineRouter, setupEnv } from 'xpine';
271
+
272
+ await setupEnv();
273
+
274
+ export default async function startServer() {
275
+ const port = process.env.PORT || 8080;
276
+ const app = express();
277
+ app.set('view engine', 'html');
278
+
279
+ app.enable('trust proxy');
280
+ app.use(logger('dev'));
281
+ app.use(express.json());
282
+ app.use(express.urlencoded({ extended: true, }));
283
+ app.use(cookieParser());
284
+
285
+ await createXPineRouter(app);
286
+
287
+ app.set('port', port);
288
+ const server = http.createServer(app);
289
+ server.listen(port, () => {
290
+ console.info(`Server listening on port ${port}`);
291
+ });
292
+ return {
293
+ app,
294
+ server,
295
+ };
296
+ }
297
+ ```
298
+
299
+ ### xpine.config.mjs file
300
+
301
+ Add an xpine.config.mjs file to your root directory. This is used primarily for configuring file paths
302
+
303
+ ```
304
+ export default {}
305
+ ```
62
306
 
63
307
  ### SPA interactivity
64
308
 
@@ -69,29 +313,58 @@ setupEnv also supports AWS secrets manager. Simply add SECRETS_NAME=your_aws_sec
69
313
  - data-spa-crossorigin="true"
70
314
  - enables cross-origin spa navigation requests (untested)
71
315
 
72
- #### Custom events
73
- - spa-update-page-content
74
- - sent when the page content has update
75
- - spa-update-page-url
76
- - sent when the page URL has update
77
- - spa-link-click
78
- - sent when link initially gets clicked and when link is done updating content
79
- - the "state" value in the event detail will be "start" or "end"
80
- - breakpoint-change
81
- - sent when a breakpoint is changed via your Tailwind css file's breakpoints in the @theme directive, e.g.
82
- ```
83
- @theme {
84
- --breakpoint-xl: 1184px;
85
- --breakpoint-sm: 640px;
86
- --breakpoint-md: 768px;
87
- --breakpoint-lg: 1024px;
88
- }
89
- ```
90
- - this will send a custom event with the detail being the breakpoint, such as `{ breakpoint: 'xl' }`
91
- - note this will only send an event based on the values of the breakpoints in your @theme configuration
316
+ ## API
317
+
318
+ ### Build command
319
+
320
+ `xpine-build`
321
+
322
+ ### Dev server command
323
+
324
+ `xpine-dev`
325
+
326
+ ### Auth
327
+
328
+ `import { signUser, verifyUser } from 'xpine';`
329
+
330
+ ### Config
331
+
332
+ `import { config } from 'xpine';`
333
+
334
+ ### Env
335
+
336
+ ```
337
+ import { setupEnv } from 'xpine';
338
+
339
+ await setupEnv();
340
+
341
+ ```
342
+
343
+ setupEnv also supports AWS secrets manager. Simply add SECRETS_NAME=your_aws_secret_name to your .env.{stage} file
344
+
345
+ ### Custom events
346
+ - spa-update-page-content
347
+ - sent when the page content has update
348
+ - spa-update-page-url
349
+ - sent when the page URL has update
350
+ - spa-link-click
351
+ - sent when link initially gets clicked and when link is done updating content
352
+ - the "state" value in the event detail will be "start" or "end"
353
+ - breakpoint-change
354
+ - sent when a breakpoint is changed via your Tailwind css file's breakpoints in the @theme directive, e.g.
355
+ ```
356
+ @theme {
357
+ --breakpoint-xl: 1184px;
358
+ --breakpoint-sm: 640px;
359
+ --breakpoint-md: 768px;
360
+ --breakpoint-lg: 1024px;
361
+ }
362
+ ```
363
+ - this will send a custom event with the detail being the breakpoint, such as `{ breakpoint: 'xl' }`
364
+ - note this will only send an event based on the values of the breakpoints in your @theme configuration
92
365
 
93
- #### Custom scripts for certain pages
366
+ ### Custom scripts for certain pages
94
367
 
95
368
  1. Add script to src/public/scripts/pages/your_script.ts
96
- 2. Import script into page HTML (e.g. <script src="/scripts/pages/your_script.ts">)
369
+ 2. Import script into page HTML (e.g. `<script src="/scripts/pages/your_script.ts">`)
97
370
  3. To unload event listeners, use `window.addEventListener('spa-update-page-url', () => { remove event listeners here})` in the code
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();
@@ -1 +1 @@
1
- {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAgB,OAAO,EAAqB,MAAM,SAAS,CAAC;AAuJ5E,wBAAsB,YAAY;;;GA8BjC;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
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAgB,OAAO,EAAqB,MAAM,SAAS,CAAC;AAsJ5E,wBAAsB,YAAY;;;GA8BjC;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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xpine",
3
- "version": "0.0.44",
3
+ "version": "0.0.45",
4
4
  "main": "dist/index.js",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-secrets-manager": "^3.758.0",