xpine 0.0.2 → 0.0.3

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/.d.ts CHANGED
@@ -1 +1 @@
1
- declare module 'xpine';
1
+ declare module 'xpine';
package/TODO ADDED
@@ -0,0 +1,2 @@
1
+ - npx create-xpine-app
2
+ - xpine-dev
package/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './src/runDevServer.js';
2
+ export * from './src/express.js';
3
+ export * from './src/util/env.js';
4
+ export * from './src/util/get-config';
5
+ export * from './src/scripts/build.js';
6
+ export * from './src/auth.ts';
7
+ export * from './src/util/html.js';
@@ -0,0 +1 @@
1
+ declare module 'xpine/jsx-runtime';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "xpine",
3
- "version": "0.0.2",
4
- "main": "index.mjs",
3
+ "version": "0.0.3",
4
+ "main": "dist/index.js",
5
5
  "dependencies": {
6
6
  "@tailwindcss/postcss": "^4.0.8",
7
7
  "builtin-modules": "^4.0.0",
@@ -19,8 +19,16 @@
19
19
  "tsx": "^4.19.3",
20
20
  "typescript": "^5.7.3"
21
21
  },
22
+ "scripts": {
23
+ "build": "tsx ./src/scripts/build-module.ts"
24
+ },
22
25
  "bin": {
23
- "xpine-build": "src/scripts/build.mjs"
26
+ "xpine-build": "dist/src/scripts/xpine-build.js",
27
+ "xpine-dev": "dist/src/scripts/xpine-dev.js"
24
28
  },
25
- "type": "module"
29
+ "type": "module",
30
+ "devDependencies": {
31
+ "@types/express": "^5.0.0",
32
+ "@types/node": "^22.13.5"
33
+ }
26
34
  }
@@ -1,7 +1,8 @@
1
1
  import jsonwebtoken from 'jsonwebtoken';
2
+ import { ServerRequest } from '../types';
2
3
  const { verify, sign, } = jsonwebtoken;
3
4
 
4
- export async function signUser(email, username) {
5
+ export async function signUser(email: string, username: string) {
5
6
  return new Promise((resolve, reject) => {
6
7
  sign(
7
8
  {
@@ -20,7 +21,7 @@ export async function signUser(email, username) {
20
21
  });
21
22
  }
22
23
 
23
- export async function verifyUser(token) {
24
+ export async function verifyUser(token: string): Promise<any> {
24
25
  return new Promise((resolve, reject) => {
25
26
  // @ts-ignore
26
27
  verify(token, process.env.JWT_PRIVATE_KEY, (err, authorizedData) => {
@@ -30,10 +31,10 @@ export async function verifyUser(token) {
30
31
  });
31
32
  }
32
33
 
33
- export function getTokenFromRequest(req) {
34
+ export function getTokenFromRequest(req: ServerRequest) {
34
35
  // @ts-ignore
35
36
  const { authorization, } = req.headers;
36
37
  if (!authorization) return null;
37
38
  const token = authorization.split(' ').pop() || '';
38
39
  return token || null;
39
- }
40
+ }
@@ -2,9 +2,13 @@ import ts from 'typescript';
2
2
  import fs from 'fs-extra';
3
3
  import path from 'path';
4
4
 
5
+ type ImportDeclaration = {
6
+ node: ts.Node;
7
+ importPath: string;
8
+ }
5
9
 
6
10
  // node arg should initially be the source node
7
- export function getImportDeclarationNodes(node, sourceFile, importDeclarations = []) {
11
+ export function getImportDeclarationNodes(node: ts.Node, sourceFile: ts.SourceFile, importDeclarations: ImportDeclaration[] = []) {
8
12
  if (node.kind === ts.SyntaxKind.ImportDeclaration) {
9
13
  let importPath = null;
10
14
  node.forEachChild(child => {
@@ -23,7 +27,7 @@ export function getImportDeclarationNodes(node, sourceFile, importDeclarations =
23
27
  return importDeclarations;
24
28
  }
25
29
 
26
- export function stripImportsNodesFromFile(importsToStrip, content, importDeclarations) {
30
+ export function stripImportsNodesFromFile(importsToStrip: string[], content: string, importDeclarations: ImportDeclaration[]) {
27
31
  let outputContent = content;
28
32
  const filteredImports = importDeclarations.filter(item => importsToStrip.includes(item.importPath));
29
33
  for (const importItem of filteredImports) {
@@ -32,11 +36,17 @@ export function stripImportsNodesFromFile(importsToStrip, content, importDeclara
32
36
  return outputContent;
33
37
  }
34
38
 
39
+ type FoundAlpineFunction = {
40
+ name: string;
41
+ isDefaultExport: boolean;
42
+ hasExport: boolean;
43
+ }
44
+
35
45
  export function findDataAttributesAndFunctions(
36
- node,
37
- sourceFile,
38
- foundDataAttributes = [],
39
- foundFunctions = []
46
+ node: ts.Node,
47
+ sourceFile: ts.SourceFile,
48
+ foundDataAttributes: string[] = [],
49
+ foundFunctions: FoundAlpineFunction[] = []
40
50
  ) {
41
51
  if (node.kind === ts.SyntaxKind.JsxAttribute) {
42
52
  const attribute = getAttributeValuePair(node, sourceFile);
@@ -64,7 +74,7 @@ export function findDataAttributesAndFunctions(
64
74
  };
65
75
  }
66
76
 
67
- export function getAttributeValuePair(node, sourceFile) {
77
+ export function getAttributeValuePair(node: ts.Node, sourceFile: ts.SourceFile): string[] {
68
78
  const result = [];
69
79
  node.forEachChild(child => {
70
80
  result.push(child.getText(sourceFile));
@@ -72,7 +82,7 @@ export function getAttributeValuePair(node, sourceFile) {
72
82
  return result;
73
83
  }
74
84
 
75
- export function getFunctionDeclarationValue(node, sourceFile) {
85
+ export function getFunctionDeclarationValue(node: ts.Node, sourceFile: ts.SourceFile): FoundAlpineFunction {
76
86
  const result = {
77
87
  name: null,
78
88
  isDefaultExport: false,
@@ -93,7 +103,7 @@ export function getFunctionDeclarationValue(node, sourceFile) {
93
103
  }
94
104
 
95
105
  // Convert entry point files into one big bundle file
96
- export function convertEntryPointsToSingleFile(entryPoints, tempWritePath) {
106
+ export function convertEntryPointsToSingleFile(entryPoints: string[], tempWritePath: string) {
97
107
  fs.writeFileSync(
98
108
  tempWritePath,
99
109
  entryPoints
@@ -102,15 +112,15 @@ export function convertEntryPointsToSingleFile(entryPoints, tempWritePath) {
102
112
  );
103
113
  }
104
114
 
105
- export function removeClientScriptInTSXFile(pathName) {
115
+ export function removeClientScriptInTSXFile(pathName: string) {
106
116
  const content = fs.readFileSync(pathName, 'utf-8');
107
117
  const source = ts.createSourceFile(
108
118
  pathName,
109
119
  content,
110
120
  ts.ScriptTarget.Latest
111
121
  );
112
- let toRemoveFrom;
113
- let clientDataStart;
122
+ let toRemoveFrom: number;
123
+ let clientDataStart: number;
114
124
  const clientImportsToHoist = [];
115
125
  const clientImportsToReplace = [];
116
126
  source.forEachChild((child) => {
@@ -157,7 +167,7 @@ export function removeClientScriptInTSXFile(pathName) {
157
167
  }
158
168
 
159
169
  export function printRecursiveFrom(
160
- node, indentLevel, sourceFile
170
+ node: ts.Node, indentLevel: number, sourceFile: ts.SourceFile
161
171
  ) {
162
172
  const indentation = '-'.repeat(indentLevel);
163
173
  const syntaxKind = ts.SyntaxKind[node.kind];
@@ -1,9 +1,9 @@
1
- import express from 'express';
1
+ import express, { NextFunction, Express, Request, Response } from 'express';
2
2
  import { globSync } from 'glob';
3
- import { config } from './util/get-config.mjs';
4
- import { verifyUser } from './auth.mjs';
5
- import require from './util/require.mjs';
3
+ import { config } from './util/get-config';
4
+ import { verifyUser } from './auth';
6
5
  import requestIP from 'request-ip';
6
+ import { ServerRequest } from '../types';
7
7
 
8
8
  const doctypeHTML = '<!DOCTYPE html>';
9
9
 
@@ -23,7 +23,7 @@ export async function createRouter() {
23
23
  for (const route of routeMap) {
24
24
  const isJSX = route.originalRoute.endsWith('.tsx') || route.originalRoute.endsWith('.jsx');
25
25
  // Import route
26
- const routeItem = process.env.NODE_ENV === 'development' ? null : require(route.path).default;
26
+ const routeItem = process.env.NODE_ENV === 'development' ? null : (await import(route.path)).default;
27
27
  // Configure result,methods for the route
28
28
  const slugRoute = route.route.toLowerCase().replace(/[ ]/g, '');
29
29
  const foundMethod = methods.find(method => slugRoute.endsWith(`.${method}`));
@@ -43,7 +43,7 @@ export async function createRouter() {
43
43
  foundMethod,
44
44
  route,
45
45
  });
46
- router[foundMethod || 'get'](formattedRouteItem, async (req, res) => {
46
+ router[foundMethod || 'get'](formattedRouteItem, async (req: Request, res: Response) => {
47
47
  try {
48
48
  // Check if it's a string response from the routeItem or is a different response
49
49
  if (routeItem) {
@@ -54,7 +54,7 @@ export async function createRouter() {
54
54
  }
55
55
  return;
56
56
  }
57
- const defaultRouteImport = require(route.path).default;
57
+ const defaultRouteImport = (await import(route.path + `?cache=${Date.now()}`)).default;
58
58
  // Require every time only if in development mode
59
59
  if (isJSX) {
60
60
  res.send(doctypeHTML + (await defaultRouteImport(req, res)));
@@ -73,7 +73,7 @@ export async function createRouter() {
73
73
  };
74
74
  }
75
75
 
76
- export async function verifyUserMiddleware(req, _res, next) {
76
+ async function verifyUserMiddleware(req: ServerRequest, _res: Response, next: NextFunction) {
77
77
  // @ts-ignore
78
78
  const { usertoken, } = req.cookies;
79
79
  if (!usertoken) {
@@ -88,28 +88,31 @@ export async function verifyUserMiddleware(req, _res, next) {
88
88
  next();
89
89
  }
90
90
 
91
- export async function createXPineRouter(app, beforeErrorRoute) {
91
+ export async function createXPineRouter(app: any, beforeErrorRoute?: (app: Express) => void) {
92
92
  app.use(express.static(config.distPublicDir));
93
93
  app.use(verifyUserMiddleware);
94
94
  app.use(requestIP.mw());
95
95
 
96
96
  const { router, routeResults, } = await createRouter();
97
- app.use(function replaceableRouter(req, res, next) {
97
+ app.use(function replaceableRouter(req: Request, res: Response, next: NextFunction) {
98
98
  router(req, res, next);
99
99
  });
100
100
 
101
101
  const found404 = routeResults?.find(item => item?.formattedRouteItem === '/404');
102
- const import404 = found404 ? require(found404.route.path).default : null;
102
+ const import404 = process.env.NODE_ENV === 'development' ? null : (await import(found404.route.path)).default;
103
103
 
104
104
  if (beforeErrorRoute) beforeErrorRoute(app);
105
105
 
106
106
  // error handler
107
- app.use(async function (req, res) {
107
+ app.use(async function (req: Request, res: Response) {
108
108
  // render the error page
109
109
  res.status(404);
110
110
 
111
111
  if (import404) {
112
112
  res.send(doctypeHTML + (await import404(req, res)));
113
+ } else if (found404 && process.env.NODE_ENV === 'development') {
114
+ const import404Item = (await import(found404.route.path + `?cache=${Date.now()}`)).default
115
+ res.send(doctypeHTML + (await import404Item(req, res)));
113
116
  } else {
114
117
  res.send('Error');
115
118
  }
@@ -3,14 +3,16 @@ import http from 'http';
3
3
  import express from 'express';
4
4
  import EventEmitter from 'events';
5
5
  import chokidar from 'chokidar';
6
- import { buildApp } from './build.mjs';
7
- import { globSync } from 'glob';
6
+ import { buildApp } from './scripts/build.js';
8
7
  import path from 'path';
9
- import { config } from './util/get-config.mjs';
10
- import require from './util/require.mjs';
8
+ import { config } from './util/get-config';
9
+ import { setupEnv } from './util/env.js';
10
+
11
+ setupEnv();
11
12
 
12
13
  export async function runDevServer() {
13
- const startServer = require(config.serverAppPath);
14
+ process.env.NODE_ENV = 'development';
15
+ const startServer = await import(config.serverDistAppPath + `?cache=${Date.now()}`);
14
16
 
15
17
  // Initial server set up
16
18
  await buildApp(true);
@@ -29,19 +31,10 @@ export async function runDevServer() {
29
31
  // We modified files in the server, restart the server
30
32
  appServer.server.close();
31
33
  await buildApp(true);
32
- // Clear requires for startServer, then restart the server
33
- delete require.cache[config.serverAppPath];
34
- const startServer = require(config.serverAppPath);
34
+ const startServer = await import(config.serverDistAppPath + `?cache=${Date.now()}`);
35
35
  appServer = await startServer.default();
36
36
  return;
37
37
  }
38
- // Clear all import cache for components
39
- const files = globSync(config.distDir + '/**/*.{tsx,ts,js,jsx}', {
40
- ignore: [config.serverDistDir + '/**/*.*'],
41
- });
42
- for (const file of files) {
43
- delete require.cache[file];
44
- }
45
38
  await buildApp(true);
46
39
  refreshEmitter.emit('refresh');
47
40
  });
@@ -0,0 +1,36 @@
1
+ import fs from 'fs-extra';
2
+ import { build } from 'esbuild';
3
+ import { config } from '../util/get-config.js';
4
+ import path from 'path';
5
+
6
+ // Extensions to look for in the bundle
7
+ const packageJson = JSON.parse(fs.readFileSync(config.packageJsonPath, 'utf-8'));
8
+ const allPackages = Object.keys(packageJson.devDependencies).concat(Object.keys(packageJson.dependencies));
9
+ const dirname = import.meta.dirname;
10
+
11
+ export async function buildXPine() {
12
+ await build({
13
+ entryPoints: [
14
+ // The contents of XPine itself
15
+ path.join(dirname, '../../index.ts'),
16
+ // The script for xpine-build
17
+ path.join(dirname, './xpine-build.ts'),
18
+ // The script for xpine-dev
19
+ path.join(dirname, './xpine-dev.ts'),
20
+ // Static files
21
+ path.join(dirname, '../static/dev-server.mjs'),
22
+ path.join(dirname, '../static/spa.mjs'),
23
+ ],
24
+ format: 'esm',
25
+ platform: 'node',
26
+ outdir: path.join(dirname, '../../dist'),
27
+ bundle: true,
28
+ sourcemap: 'inline',
29
+ external: allPackages,
30
+ jsx: 'transform',
31
+ minify: false,
32
+ plugins: [],
33
+ });
34
+ }
35
+
36
+ buildXPine();
@@ -7,13 +7,12 @@ import {
7
7
  convertEntryPointsToSingleFile,
8
8
  findDataAttributesAndFunctions,
9
9
  removeClientScriptInTSXFile
10
- } from './build/typescript-builder.mjs';
10
+ } from '../build/typescript-builder.js';
11
11
  import { globSync } from 'glob';
12
12
  import postcss from 'postcss';
13
13
  // @ts-ignore
14
14
  import tailwindPostcss from '@tailwindcss/postcss';
15
- import { config } from './util/get-config.mjs';
16
- import basePath from '../base-path.mjs';
15
+ import { config } from '../util/get-config';
17
16
 
18
17
  // Extensions to look for in the bundle
19
18
  const extensions = ['.ts', '.tsx'];
@@ -36,7 +35,7 @@ export async function buildApp(isDev = false) {
36
35
  }
37
36
  }
38
37
 
39
- async function buildAppFiles(files, isDev) {
38
+ async function buildAppFiles(files: string[], isDev?: boolean) {
40
39
  const componentData = [];
41
40
  const dataFiles = [];
42
41
  // Filter out client side files and files that aren't of the allowed extensions
@@ -125,7 +124,7 @@ async function buildAppFiles(files, isDev) {
125
124
  }
126
125
 
127
126
  // Build client side files
128
- async function buildClientSideFiles(alpineDataFiles = [], isDev) {
127
+ async function buildClientSideFiles(alpineDataFiles: string[] = [], isDev?: boolean) {
129
128
  // Write the temp file to use
130
129
  const tempFilePath = path.join(config.distTempFolder, './app.ts');
131
130
  fs.ensureFileSync(tempFilePath);
@@ -162,19 +161,19 @@ async function buildClientSideFiles(alpineDataFiles = [], isDev) {
162
161
  await logSize(config.distPublicDir, 'client');
163
162
  }
164
163
 
165
- function writeDevServerClientSideCode(tempFilePath) {
166
- const devServerPath = path.join(basePath, './src/build/dev-server.mjs');
164
+ function writeDevServerClientSideCode(tempFilePath: string) {
165
+ const devServerPath = path.join(import.meta.dirname, '../static/dev-server.js');
167
166
  const content = fs.readFileSync(devServerPath, 'utf-8');
168
167
  fs.appendFileSync(tempFilePath, `\n` + content);
169
168
  }
170
169
 
171
- function writeSpaClientSideCode(tempFilePath) {
172
- const spaPath = path.join(basePath, './src/build/spa.mjs');
170
+ function writeSpaClientSideCode(tempFilePath: string) {
171
+ const spaPath = path.join(import.meta.dirname, '../static/spa.js');
173
172
  const content = fs.readFileSync(spaPath, 'utf-8');
174
173
  fs.appendFileSync(tempFilePath, `\n` + content);
175
174
  }
176
175
 
177
- async function buildAlpineDataFile(componentData, dataFiles) {
176
+ async function buildAlpineDataFile(componentData: any[], dataFiles: any[]) {
178
177
  const output = {
179
178
  imports: [
180
179
  'import Alpine from \'alpinejs\';'
@@ -254,7 +253,12 @@ export async function buildPublicFolderSymlinks() {
254
253
  }
255
254
  }
256
255
 
257
- export async function logSize(pathName, type, validExtensions = ['.js', '.css']) {
256
+ type FileItem = {
257
+ file: string;
258
+ size: number;
259
+ }
260
+
261
+ export async function logSize(pathName: string, type: 'app' | 'client' | 'css', validExtensions = ['.js', '.css']) {
258
262
  const files = globSync(pathName + '/**/*' + (type === 'css' ? '.css' : ''));
259
263
  const fileSizes = files.map((file) => {
260
264
  if (!validExtensions.find(ext => file.endsWith(ext))) return false;
@@ -263,7 +267,7 @@ export async function logSize(pathName, type, validExtensions = ['.js', '.css'])
263
267
  size: (fs.statSync(file).size) / (1024 * 1000),
264
268
  };
265
269
  }).filter(Boolean);
266
- const totalSize = fileSizes.reduce((total, current) => {
270
+ const totalSize = fileSizes.reduce((total, current: FileItem) => {
267
271
  return current.size + total;
268
272
  }, 0);
269
273
  console.info(`[${totalSize.toFixed(3)} MB] Built ${type}`);
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { buildApp } from "./build.js";
4
+
5
+ buildApp();
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runDevServer } from "../runDevServer";
4
+
5
+ runDevServer();
@@ -1,6 +1,6 @@
1
1
  import dotenv from 'dotenv';
2
2
  import path from 'path';
3
- import { config } from './get-config.mjs';
3
+ import { config } from './get-config';
4
4
 
5
5
  export function setupEnv() {
6
6
  dotenv.config({ path: path.join(config.rootDir, `./.env.${process.env.STAGE || 'dev'}`),});
@@ -1,9 +1,10 @@
1
1
  import path from 'path';
2
- import require from './require.mjs';
2
+ import require from './require.js';
3
+ import { XPineConfig } from '../../types';
3
4
 
4
5
  const rootDir = process.cwd();
5
6
 
6
- export function fromRoot(pathName) {
7
+ export function fromRoot(pathName?: string) {
7
8
  if (!pathName) return rootDir;
8
9
  return path.join(rootDir, pathName);
9
10
  }
@@ -34,6 +35,9 @@ const configDefaults = {
34
35
  get serverDistDir() {
35
36
  return path.join(this.distDir, './server');
36
37
  },
38
+ get serverDistAppPath() {
39
+ return path.join(this.serverDistDir, './app.js');
40
+ },
37
41
  // Important dirs/paths
38
42
  get pagesDir() {
39
43
  return path.join(this.srcDir, './pages');
@@ -55,7 +59,7 @@ const configDefaults = {
55
59
  },
56
60
  };
57
61
 
58
- export const config = {
62
+ export const config: XPineConfig = {
59
63
  ...configDefaults,
60
64
  ...userConfig,
61
- }
65
+ };
package/tsconfig.json CHANGED
@@ -19,9 +19,9 @@
19
19
  "moduleResolution": "node",
20
20
  "resolveJsonModule": true,
21
21
  "isolatedModules": true,
22
- "jsxImportSource": "html",
22
+ "jsxImportSource": "xpine",
23
23
  "incremental": true,
24
- "allowImportingTsExtensions": true,
24
+ "allowImportingTsExtensions": true
25
25
  },
26
26
  "include": [
27
27
  "**/*.ts",
@@ -29,17 +29,17 @@
29
29
  "postcss.config.mjs",
30
30
  "tailwind.config.js",
31
31
  "eslint.config.mjs",
32
- "index.mjs",
33
- "src/runDevServer.mjs",
34
- "src/express.mjs",
35
- "src/build.mjs",
36
- "src/auth.mjs",
37
- "src/util/require.mjs",
38
- "src/util/get-config.mjs",
39
- "src/util/env.mjs",
40
- "src/scripts/run-dev.mjs",
41
- "src/scripts/build.mjs",
42
- "src/build/typescript-builder.mjs"
32
+ "index.ts",
33
+ "src/runDevServer.ts",
34
+ "src/express.ts",
35
+ "src/scripts/build.ts",
36
+ "src/auth.ts",
37
+ "src/util/require.ts",
38
+ "src/util/get-config.ts",
39
+ "src/util/env.ts",
40
+ "src/scripts/xpine-dev.ts",
41
+ "src/scripts/xpine-build.ts",
42
+ "src/build/typescript-builder.ts"
43
43
  ],
44
44
  "exclude": [
45
45
  "node_modules",
package/types.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { Request } from 'express';
2
+
3
+ export type XPineConfig = {
4
+ [key: string]: string;
5
+ }
6
+
7
+ export type TokenUser = {
8
+ email?: string;
9
+ username?: string;
10
+ }
11
+
12
+ export type ServerRequest = Request & {
13
+ user?: TokenUser;
14
+ }
@@ -0,0 +1 @@
1
+ export default {};
package/base-path.mjs DELETED
@@ -1,5 +0,0 @@
1
- import path from 'path';
2
- import { fileURLToPath } from 'url';
3
- const filename = fileURLToPath(import.meta.url);
4
- const basePath = path.dirname(filename);
5
- export default basePath;
package/index.mjs DELETED
@@ -1,7 +0,0 @@
1
- export * from './src/runDevServer.mjs';
2
- export * from './src/express.mjs';
3
- export * from './src/util/env.mjs';
4
- export * from './src/util/get-config.mjs';
5
- export * from './src/build.mjs';
6
- export * from './src/auth.mjs';
7
- export * from './src/util/html.mjs';
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { buildApp } from "../build.mjs";
4
-
5
- buildApp();
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { runDevServer } from "../runDevServer.mjs";
4
-
5
- runDevServer();
File without changes
File without changes
File without changes
File without changes