xpine 0.0.59 → 0.0.60

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.
Files changed (37) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/.claude/worktrees/happy-lalande-c6c39c/.claude/settings.local.json +33 -0
  3. package/.claude/worktrees/happy-lalande-c6c39c/.d.ts +1 -0
  4. package/.claude/worktrees/happy-lalande-c6c39c/.gitattributes +2 -0
  5. package/.claude/worktrees/happy-lalande-c6c39c/README.md +571 -0
  6. package/.claude/worktrees/happy-lalande-c6c39c/TODO +2 -0
  7. package/.claude/worktrees/happy-lalande-c6c39c/eslint.config.mjs +27 -0
  8. package/.claude/worktrees/happy-lalande-c6c39c/jsx-runtime.d.ts +1 -0
  9. package/.claude/worktrees/happy-lalande-c6c39c/package-lock.json +5862 -0
  10. package/.claude/worktrees/happy-lalande-c6c39c/package.json +62 -0
  11. package/.claude/worktrees/happy-lalande-c6c39c/tsconfig.json +43 -0
  12. package/.claude/worktrees/happy-lalande-c6c39c/types.ts +55 -0
  13. package/.claude/worktrees/happy-lalande-c6c39c/xpine.config.mjs +1 -0
  14. package/.claude/worktrees/jolly-hofstadter-2764be/.claude/settings.local.json +33 -0
  15. package/.claude/worktrees/jolly-hofstadter-2764be/.d.ts +1 -0
  16. package/.claude/worktrees/jolly-hofstadter-2764be/.gitattributes +2 -0
  17. package/.claude/worktrees/jolly-hofstadter-2764be/README.md +571 -0
  18. package/.claude/worktrees/jolly-hofstadter-2764be/TODO +2 -0
  19. package/.claude/worktrees/jolly-hofstadter-2764be/eslint.config.mjs +27 -0
  20. package/.claude/worktrees/jolly-hofstadter-2764be/jsx-runtime.d.ts +1 -0
  21. package/.claude/worktrees/jolly-hofstadter-2764be/package-lock.json +5862 -0
  22. package/.claude/worktrees/jolly-hofstadter-2764be/package.json +62 -0
  23. package/.claude/worktrees/jolly-hofstadter-2764be/tsconfig.json +43 -0
  24. package/.claude/worktrees/jolly-hofstadter-2764be/types.ts +55 -0
  25. package/.claude/worktrees/jolly-hofstadter-2764be/xpine.config.mjs +1 -0
  26. package/README.md +11 -0
  27. package/create-xpine-app/README.md +20 -0
  28. package/create-xpine-app/index.js +80 -0
  29. package/create-xpine-app/package.json +30 -0
  30. package/create-xpine-app/template/README.md +50 -0
  31. package/create-xpine-app/template/eslint.config.mjs +27 -0
  32. package/create-xpine-app/template/gitignore +5 -0
  33. package/create-xpine-app/template/package.json +32 -0
  34. package/create-xpine-app/template/tsconfig.json +36 -0
  35. package/create-xpine-app/template/xpine.config.mjs +1 -0
  36. package/eslint.config.mjs +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "xpine",
3
+ "version": "0.0.59",
4
+ "main": "dist/index.js",
5
+ "dependencies": {
6
+ "@aws-sdk/client-secrets-manager": "^3.758.0",
7
+ "@tailwindcss/postcss": "^4.1.12",
8
+ "builtin-modules": "^4.0.0",
9
+ "chokidar": "^4.0.3",
10
+ "dotenv": "^16.4.7",
11
+ "esbuild": "^0.25.0",
12
+ "express": "^4.21.2",
13
+ "express-ws": "^5.0.2",
14
+ "fs-extra": "^11.3.0",
15
+ "glob": "^11.0.1",
16
+ "jsonwebtoken": "^9.0.2",
17
+ "micromatch": "^4.0.8",
18
+ "minify-xml": "^4.5.2",
19
+ "postcss": "^8.5.3",
20
+ "request-ip": "^3.3.0",
21
+ "shelljs": "^0.9.2",
22
+ "tsx": "^4.19.3",
23
+ "typescript": "^5.7.3",
24
+ "yargs": "^17.7.2"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git://github.com/dan-caldwell/xpine.git"
29
+ },
30
+ "scripts": {
31
+ "build": "tsx ./src/scripts/run-build-module.ts && npm run build:types",
32
+ "build:types": "sh ./src/scripts/build-types.sh",
33
+ "xpine-dev-server": "tsx ./src/scripts/xpine-dev-server.ts",
34
+ "test": "cd ./tests && npm run test",
35
+ "test:dev": "cd ./tests && npm run dev",
36
+ "test:start": "cd ./tests && npm run build && npm start",
37
+ "test:build": "cd ./tests && npm run build",
38
+ "test:build:dev": "cd ./tests && npm run build:dev",
39
+ "lint": "npx eslint",
40
+ "fix": "npx eslint --fix",
41
+ "release:patch": "npm run build && npm version patch && npm publish"
42
+ },
43
+ "bin": {
44
+ "xpine-build": "dist/src/scripts/xpine-build.js",
45
+ "xpine-dev": "dist/src/scripts/xpine-dev.js"
46
+ },
47
+ "types": "dist/index.d.ts",
48
+ "type": "module",
49
+ "devDependencies": {
50
+ "@eslint/js": "^9.24.0",
51
+ "@tailwindcss/oxide-darwin-arm64": "^4.1.3",
52
+ "@types/express": "^5.0.0",
53
+ "@types/micromatch": "^4.0.9",
54
+ "@types/node": "^22.13.5",
55
+ "@types/request-ip": "^0.0.41",
56
+ "@types/shelljs": "^0.8.15",
57
+ "@types/yargs": "^17.0.33",
58
+ "axios": "^1.8.4",
59
+ "lightningcss-darwin-arm64": "^1.29.3",
60
+ "typescript-eslint": "^8.29.0"
61
+ }
62
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "preserve",
4
+ "jsxFragmentFactory": "html.fragment",
5
+ "jsxFactory": "html.createElement",
6
+ "lib": [
7
+ "dom",
8
+ "dom.iterable",
9
+ "esnext"
10
+ ],
11
+ "allowJs": false,
12
+ "skipLibCheck": true,
13
+ "strict": false,
14
+ "forceConsistentCasingInFileNames": false,
15
+ "noEmit": true,
16
+ "esModuleInterop": true,
17
+ "target": "esnext",
18
+ "module": "ESNext",
19
+ "moduleResolution": "node",
20
+ "resolveJsonModule": true,
21
+ "isolatedModules": true,
22
+ "jsxImportSource": "xpine",
23
+ "incremental": true,
24
+ "allowImportingTsExtensions": true,
25
+ // Generate d.ts files
26
+ "declaration": true,
27
+ "emitDeclarationOnly": true,
28
+ "outDir": "dist",
29
+ "declarationMap": true,
30
+ "types": []
31
+ },
32
+ "include": [
33
+ "src/**/*.ts"
34
+ ],
35
+ "exclude": [
36
+ "node_modules",
37
+ "./node_modules",
38
+ "./node_modules/*",
39
+ "./node_modules/@types/node/index.d.ts",
40
+ "dist",
41
+ "**/*.js"
42
+ ],
43
+ }
@@ -0,0 +1,55 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ import ts from 'typescript';
3
+
4
+ export type XPineConfig = {
5
+ [key: string]: any;
6
+ }
7
+
8
+ export type TokenUser = {
9
+ email?: string;
10
+ username?: string;
11
+ }
12
+
13
+ export type ServerRequest = Request & {
14
+ user?: TokenUser;
15
+ clientIp?: string;
16
+ }
17
+
18
+ export type WrapperProps = {
19
+ req: ServerRequest;
20
+ children: any;
21
+ config: ConfigFile;
22
+ data?: any;
23
+ routePath?: string;
24
+ }
25
+
26
+ export type ConfigFile = {
27
+ staticPaths?: boolean | (() => Promise<{ [key: string]: string }[]> | { [key: string]: string }[]);
28
+ wrapper?: (props: WrapperProps) => Promise<any>;
29
+ data?: (req: ServerRequest) => Promise<any>;
30
+ routeMiddleware?: (req: ServerRequest, res: Response, next: NextFunction) => void;
31
+ // For multi-segment dynamic routes ([...slug]): validate a slug that was not
32
+ // generated at build time so it can be resolved safely at request time.
33
+ // Return false (the default for unknown slugs) to fall through to the 404 handler.
34
+ isValid?: (slug: string, req: ServerRequest) => boolean | Promise<boolean>;
35
+ }
36
+
37
+ export type PageProps = {
38
+ req: ServerRequest;
39
+ res: Response;
40
+ data: any;
41
+ routePath: string;
42
+ }
43
+
44
+ export type FileItem = {
45
+ file: string;
46
+ size: number;
47
+ }
48
+
49
+ export type ComponentData = {
50
+ path: string;
51
+ contents: string;
52
+ clientContent: string;
53
+ configFiles: string[];
54
+ source: ts.SourceFile;
55
+ }
@@ -0,0 +1 @@
1
+ export default {};
package/README.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  Combines JSX with Alpine.js for a simpler, easier development experience. Includes a static site generator.
4
4
 
5
+ ### Quick start
6
+
7
+ Scaffold a new app with pages, demo content, and an API endpoint already set up:
8
+
9
+ ```
10
+ npx create-xpine-app my-app
11
+ cd my-app
12
+ npm install
13
+ npm run dev
14
+ ```
15
+
5
16
  ### Install
6
17
 
7
18
  `npm install xpine`
@@ -0,0 +1,20 @@
1
+ # create-xpine-app
2
+
3
+ Scaffold a new [XPine](https://github.com/dan-caldwell/xpine) app (Alpine.js + JSX + Tailwind) with one command:
4
+
5
+ ```
6
+ npx create-xpine-app my-app
7
+ cd my-app
8
+ npm install
9
+ npm run dev
10
+ ```
11
+
12
+ Run `npx create-xpine-app .` to scaffold into the current directory.
13
+
14
+ The generated app includes:
15
+
16
+ - File-based routing with a shared page wrapper
17
+ - Server-side data fetching from the [JSONPlaceholder API](https://jsonplaceholder.typicode.com) (posts and users pages)
18
+ - A dynamic route (`/posts/[id]`) with static generation for known paths
19
+ - An Express API endpoint (`/api/posts`)
20
+ - Alpine.js interactivity examples and Tailwind CSS styling
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import readline from 'node:readline/promises';
6
+
7
+ const templateDir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'template');
8
+
9
+ const helpMessage = `
10
+ Usage: npx create-xpine-app [project-name]
11
+
12
+ Scaffolds a new XPine app in a directory named [project-name].
13
+ Pass "." to scaffold into the current directory.
14
+
15
+ Options:
16
+ -h, --help Show this message
17
+ `;
18
+
19
+ function exitWithError(message) {
20
+ console.error(`\x1b[31m${message}\x1b[0m`);
21
+ process.exit(1);
22
+ }
23
+
24
+ function isValidProjectName(name) {
25
+ return /^[a-z0-9~-][a-z0-9._~-]*$/.test(name);
26
+ }
27
+
28
+ function isEmptyDir(dir) {
29
+ return fs.readdirSync(dir).filter((file) => file !== '.git').length === 0;
30
+ }
31
+
32
+ async function getProjectName() {
33
+ const arg = process.argv[2];
34
+ if (arg) return arg;
35
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
36
+ const answer = await rl.question('Project name: (my-xpine-app) ');
37
+ rl.close();
38
+ return answer.trim() || 'my-xpine-app';
39
+ }
40
+
41
+ if (['-h', '--help'].includes(process.argv[2])) {
42
+ console.log(helpMessage);
43
+ process.exit(0);
44
+ }
45
+
46
+ const projectName = await getProjectName();
47
+ const useCurrentDir = projectName === '.';
48
+ const targetDir = useCurrentDir ? process.cwd() : path.resolve(process.cwd(), projectName);
49
+ const packageName = useCurrentDir ? path.basename(targetDir) : projectName;
50
+
51
+ if (!isValidProjectName(packageName)) {
52
+ exitWithError(`Invalid project name: "${packageName}". Use lowercase letters, numbers, dashes, dots, and underscores.`);
53
+ }
54
+
55
+ if (fs.existsSync(targetDir) && !isEmptyDir(targetDir)) {
56
+ exitWithError(`The directory ${useCurrentDir ? 'you are in' : `"${projectName}"`} already exists and is not empty.`);
57
+ }
58
+
59
+ console.log(`\nScaffolding XPine app in ${targetDir}...`);
60
+
61
+ fs.cpSync(templateDir, targetDir, { recursive: true });
62
+
63
+ // npm strips .gitignore files from published packages, so the template ships
64
+ // it as "gitignore" and it gets renamed here
65
+ fs.renameSync(path.join(targetDir, 'gitignore'), path.join(targetDir, '.gitignore'));
66
+
67
+ const packageJsonPath = path.join(targetDir, 'package.json');
68
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
69
+ packageJson.name = packageName;
70
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
71
+
72
+ console.log(`
73
+ Done! Next steps:
74
+ ${useCurrentDir ? '' : `
75
+ cd ${projectName}`}
76
+ npm install
77
+ npm run dev
78
+
79
+ Your app will be running at http://localhost:3000
80
+ `);
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "create-xpine-app",
3
+ "version": "0.0.1",
4
+ "description": "Scaffold a new XPine app with one command",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-xpine-app": "index.js"
8
+ },
9
+ "files": [
10
+ "index.js",
11
+ "template"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git://github.com/dan-caldwell/xpine.git",
19
+ "directory": "create-xpine-app"
20
+ },
21
+ "keywords": [
22
+ "xpine",
23
+ "create-xpine-app",
24
+ "alpinejs",
25
+ "jsx",
26
+ "tailwind",
27
+ "scaffold"
28
+ ],
29
+ "license": "ISC"
30
+ }
@@ -0,0 +1,50 @@
1
+ # My XPine App
2
+
3
+ A starter app built with [XPine](https://github.com/dan-caldwell/xpine) — Alpine.js + JSX + Tailwind. Demo content comes from the [JSONPlaceholder API](https://jsonplaceholder.typicode.com).
4
+
5
+ ## Getting started
6
+
7
+ ```
8
+ npm install
9
+ npm run dev
10
+ ```
11
+
12
+ Then open http://localhost:3000
13
+
14
+ ## Scripts
15
+
16
+ | Command | Description |
17
+ | --- | --- |
18
+ | `npm run dev` | Start the dev server with live reload |
19
+ | `npm run build` | Build the app for production (includes static page generation) |
20
+ | `npm start` | Run the production build |
21
+ | `npm run lint` | Lint the code |
22
+
23
+ ## Project structure
24
+
25
+ ```
26
+ src/
27
+ components/ Shared JSX components (Base layout, Navbar)
28
+ pages/ File-based routes
29
+ +config.tsx Shared page wrapper (applies to all pages in this folder and below)
30
+ index.tsx / — home page with Alpine.js examples
31
+ posts/
32
+ index.tsx /posts — post list fetched server-side
33
+ [id].tsx /posts/:id — dynamic route with static generation for known ids
34
+ users/
35
+ index.tsx /users
36
+ api/
37
+ posts.GET.ts /api/posts — Express API endpoint
38
+ 404.tsx Not-found page
39
+ public/
40
+ scripts/ Client-side entry (starts Alpine.js)
41
+ styles/ Global CSS (Tailwind)
42
+ server/ Express server setup
43
+ util/ Shared server-side helpers
44
+ ```
45
+
46
+ ## Learn more
47
+
48
+ - Page routing, static generation, API endpoints, and more: [XPine README](https://github.com/dan-caldwell/xpine#readme)
49
+ - Alpine.js docs: https://alpinejs.dev
50
+ - Tailwind CSS docs: https://tailwindcss.com
@@ -0,0 +1,27 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+
4
+ export default tseslint.config(
5
+ eslint.configs.recommended,
6
+ tseslint.configs.recommended,
7
+ {
8
+ rules: {
9
+ 'eol-last': 2,
10
+ 'quotes': ['error', 'single'],
11
+ 'comma-dangle': ['error', {
12
+ 'objects': 'always',
13
+ 'arrays': 'never',
14
+ 'imports': 'never',
15
+ 'exports': 'never',
16
+ 'functions': 'never',
17
+ }],
18
+ 'semi': 'error',
19
+ '@typescript-eslint/ban-ts-comment': 'off',
20
+ '@typescript-eslint/no-require-imports': 'off',
21
+ },
22
+ files: ['src/**/*.{js,mjs,ts,tsx,jsx}'],
23
+ },
24
+ {
25
+ ignores: ['dist/'],
26
+ }
27
+ );
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ .env*
4
+ *.tsbuildinfo
5
+ .DS_Store
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "xpine-app",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "PORT=3000 LOCALHOST=1 xpine-dev",
8
+ "build": "xpine-build --removePreviousBuild",
9
+ "start": "PORT=3000 node ./dist/server/run/prod.js",
10
+ "lint": "npx eslint",
11
+ "fix": "npx eslint --fix"
12
+ },
13
+ "dependencies": {
14
+ "alpinejs": "^3.14.8",
15
+ "cookie-parser": "^1.4.7",
16
+ "express": "^4.21.2",
17
+ "morgan": "^1.10.0",
18
+ "tailwindcss": "^4.1.12",
19
+ "xpine": "^0.0.59"
20
+ },
21
+ "devDependencies": {
22
+ "@eslint/js": "^9.24.0",
23
+ "@types/alpinejs": "^3.13.11",
24
+ "@types/cookie-parser": "^1.4.8",
25
+ "@types/express": "^5.0.0",
26
+ "@types/morgan": "^1.9.9",
27
+ "@types/node": "^22.13.5",
28
+ "eslint": "^9.24.0",
29
+ "typescript": "^5.7.3",
30
+ "typescript-eslint": "^8.29.0"
31
+ }
32
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "preserve",
4
+ "jsxFragmentFactory": "html.fragment",
5
+ "jsxFactory": "html.createElement",
6
+ "jsxImportSource": "xpine",
7
+ "lib": [
8
+ "dom",
9
+ "dom.iterable",
10
+ "esnext"
11
+ ],
12
+ "target": "esnext",
13
+ "module": "ESNext",
14
+ "moduleResolution": "node",
15
+ "allowJs": false,
16
+ "skipLibCheck": true,
17
+ "strict": false,
18
+ "noEmit": true,
19
+ "esModuleInterop": true,
20
+ "resolveJsonModule": true,
21
+ "isolatedModules": true,
22
+ "forceConsistentCasingInFileNames": false,
23
+ "incremental": true,
24
+ "allowImportingTsExtensions": true
25
+ },
26
+ "include": [
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ "eslint.config.mjs"
30
+ ],
31
+ "exclude": [
32
+ "node_modules",
33
+ "dist",
34
+ "**/*.js"
35
+ ]
36
+ }
@@ -0,0 +1 @@
1
+ export default {};
package/eslint.config.mjs CHANGED
@@ -22,6 +22,6 @@ export default tseslint.config(
22
22
  files: ["src/**/*.{js,mjs,ts,tsx,jsx}"],
23
23
  },
24
24
  {
25
- ignores: ["dist/", "cdk.out/", "tests/dist/", "tests/playwright-report/"]
25
+ ignores: ["dist/", "cdk.out/", "tests/dist/", "tests/playwright-report/", "create-xpine-app/"]
26
26
  }
27
27
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xpine",
3
- "version": "0.0.59",
3
+ "version": "0.0.60",
4
4
  "main": "dist/index.js",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-secrets-manager": "^3.758.0",