@kithinji/pod 1.0.0

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 (98) hide show
  1. package/build.js +22 -0
  2. package/dist/main.js +4464 -0
  3. package/dist/main.js.map +7 -0
  4. package/dist/types/add/component/component.d.ts +5 -0
  5. package/dist/types/add/component/component.d.ts.map +1 -0
  6. package/dist/types/add/component/index.d.ts +2 -0
  7. package/dist/types/add/component/index.d.ts.map +1 -0
  8. package/dist/types/add/index.d.ts +4 -0
  9. package/dist/types/add/index.d.ts.map +1 -0
  10. package/dist/types/add/module/index.d.ts +2 -0
  11. package/dist/types/add/module/index.d.ts.map +1 -0
  12. package/dist/types/add/module/module.d.ts +3 -0
  13. package/dist/types/add/module/module.d.ts.map +1 -0
  14. package/dist/types/add/new/index.d.ts +2 -0
  15. package/dist/types/add/new/index.d.ts.map +1 -0
  16. package/dist/types/config/config.d.ts +18 -0
  17. package/dist/types/config/config.d.ts.map +1 -0
  18. package/dist/types/config/index.d.ts +2 -0
  19. package/dist/types/config/index.d.ts.map +1 -0
  20. package/dist/types/dev/index.d.ts +2 -0
  21. package/dist/types/dev/index.d.ts.map +1 -0
  22. package/dist/types/dev/project.d.ts +9 -0
  23. package/dist/types/dev/project.d.ts.map +1 -0
  24. package/dist/types/dev/server.d.ts +2 -0
  25. package/dist/types/dev/server.d.ts.map +1 -0
  26. package/dist/types/docker/docker.d.ts +2 -0
  27. package/dist/types/docker/docker.d.ts.map +1 -0
  28. package/dist/types/docker/index.d.ts +2 -0
  29. package/dist/types/docker/index.d.ts.map +1 -0
  30. package/dist/types/macros/expand_macros.d.ts +48 -0
  31. package/dist/types/macros/expand_macros.d.ts.map +1 -0
  32. package/dist/types/macros/index.d.ts +3 -0
  33. package/dist/types/macros/index.d.ts.map +1 -0
  34. package/dist/types/macros/macro_executer.d.ts +12 -0
  35. package/dist/types/macros/macro_executer.d.ts.map +1 -0
  36. package/dist/types/main.d.ts +13 -0
  37. package/dist/types/main.d.ts.map +1 -0
  38. package/dist/types/plugins/analyzers/graph.d.ts +25 -0
  39. package/dist/types/plugins/analyzers/graph.d.ts.map +1 -0
  40. package/dist/types/plugins/css/index.d.ts +7 -0
  41. package/dist/types/plugins/css/index.d.ts.map +1 -0
  42. package/dist/types/plugins/generators/generate_controller.d.ts +2 -0
  43. package/dist/types/plugins/generators/generate_controller.d.ts.map +1 -0
  44. package/dist/types/plugins/generators/generate_rsc.d.ts +2 -0
  45. package/dist/types/plugins/generators/generate_rsc.d.ts.map +1 -0
  46. package/dist/types/plugins/generators/generate_server_component.d.ts +2 -0
  47. package/dist/types/plugins/generators/generate_server_component.d.ts.map +1 -0
  48. package/dist/types/plugins/generators/tsx_server_stub.d.ts +2 -0
  49. package/dist/types/plugins/generators/tsx_server_stub.d.ts.map +1 -0
  50. package/dist/types/plugins/index.d.ts +4 -0
  51. package/dist/types/plugins/index.d.ts.map +1 -0
  52. package/dist/types/plugins/my.d.ts +10 -0
  53. package/dist/types/plugins/my.d.ts.map +1 -0
  54. package/dist/types/plugins/transformers/j2d.d.ts +11 -0
  55. package/dist/types/plugins/transformers/j2d.d.ts.map +1 -0
  56. package/dist/types/store/index.d.ts +2 -0
  57. package/dist/types/store/index.d.ts.map +1 -0
  58. package/dist/types/store/store.d.ts +14 -0
  59. package/dist/types/store/store.d.ts.map +1 -0
  60. package/dist/types/utils/cases.d.ts +4 -0
  61. package/dist/types/utils/cases.d.ts.map +1 -0
  62. package/dist/types/utils/create.d.ts +12 -0
  63. package/dist/types/utils/create.d.ts.map +1 -0
  64. package/dist/types/utils/index.d.ts +3 -0
  65. package/dist/types/utils/index.d.ts.map +1 -0
  66. package/package.json +44 -0
  67. package/src/add/component/component.ts +496 -0
  68. package/src/add/component/index.ts +1 -0
  69. package/src/add/index.ts +3 -0
  70. package/src/add/module/index.ts +1 -0
  71. package/src/add/module/module.ts +521 -0
  72. package/src/add/new/index.ts +135 -0
  73. package/src/config/config.ts +141 -0
  74. package/src/config/index.ts +1 -0
  75. package/src/dev/index.ts +1 -0
  76. package/src/dev/project.ts +45 -0
  77. package/src/dev/server.ts +190 -0
  78. package/src/docker/docker.ts +452 -0
  79. package/src/docker/index.ts +1 -0
  80. package/src/macros/expand_macros.ts +791 -0
  81. package/src/macros/index.ts +2 -0
  82. package/src/macros/macro_executer.ts +189 -0
  83. package/src/main.ts +95 -0
  84. package/src/plugins/analyzers/graph.ts +291 -0
  85. package/src/plugins/css/index.ts +25 -0
  86. package/src/plugins/generators/generate_controller.ts +308 -0
  87. package/src/plugins/generators/generate_rsc.ts +274 -0
  88. package/src/plugins/generators/generate_server_component.ts +279 -0
  89. package/src/plugins/generators/tsx_server_stub.ts +295 -0
  90. package/src/plugins/index.ts +3 -0
  91. package/src/plugins/my.ts +274 -0
  92. package/src/plugins/transformers/j2d.ts +1014 -0
  93. package/src/store/index.ts +1 -0
  94. package/src/store/store.ts +44 -0
  95. package/src/utils/cases.ts +15 -0
  96. package/src/utils/create.ts +26 -0
  97. package/src/utils/index.ts +2 -0
  98. package/tsconfig.json +27 -0
@@ -0,0 +1,141 @@
1
+ import * as path from "path";
2
+ import { pathToFileURL } from "url";
3
+ import * as fs from "fs/promises";
4
+ import { Store } from "@/store";
5
+ import type { Plugin } from "esbuild";
6
+
7
+ export type PodPlugin = (store: Store) => Plugin;
8
+
9
+ export interface PodConfig {
10
+ name: string;
11
+ build?: {
12
+ outDir?: string;
13
+ sourcemap?: boolean;
14
+ minify?: boolean;
15
+ };
16
+ plugins?: Array<PodPlugin>;
17
+ client_plugins?: Array<PodPlugin>;
18
+ server_plugins?: Array<PodPlugin>;
19
+ }
20
+
21
+ const CONFIG_FILES = [
22
+ "pod.config.js",
23
+ "pod.config.mjs",
24
+ "pod.config.ts",
25
+ "pod.config.mts",
26
+ ];
27
+
28
+ export async function loadConfig(
29
+ root: string = process.cwd()
30
+ ): Promise<PodConfig> {
31
+ for (const configFile of CONFIG_FILES) {
32
+ const configPath = path.resolve(root, configFile);
33
+
34
+ try {
35
+ await fs.access(configPath);
36
+
37
+ if (configFile.endsWith(".ts") || configFile.endsWith(".mts")) {
38
+ return await loadTsConfig(configPath);
39
+ }
40
+
41
+ return await loadJsConfig(configPath);
42
+ } catch (error) {
43
+ continue;
44
+ }
45
+ }
46
+
47
+ return getDefaultConfig();
48
+ }
49
+
50
+ async function loadJsConfig(configPath: string): Promise<PodConfig> {
51
+ try {
52
+ const fileUrl = pathToFileURL(configPath).href;
53
+
54
+ const configModule = await import(`${fileUrl}?t=${Date.now()}`);
55
+
56
+ const config = configModule.default || configModule;
57
+
58
+ if (typeof config === "function") {
59
+ return await config();
60
+ }
61
+
62
+ return config;
63
+ } catch (error) {
64
+ console.error(`❌ Failed to load config from ${configPath}:`, error);
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ async function loadTsConfig(configPath: string): Promise<PodConfig> {
70
+ try {
71
+ const esbuild = await import("esbuild");
72
+
73
+ const result = await esbuild.build({
74
+ entryPoints: [configPath],
75
+ bundle: true,
76
+ platform: "node",
77
+ format: "esm",
78
+ write: false,
79
+ sourcemap: "inline",
80
+ packages: "external",
81
+ });
82
+
83
+ const tempFile = `${configPath}.${Date.now()}.mjs`;
84
+ await fs.writeFile(tempFile, result.outputFiles[0].text);
85
+
86
+ try {
87
+ const fileUrl = pathToFileURL(tempFile).href;
88
+
89
+ const configModule = await import(fileUrl);
90
+
91
+ const config = configModule.default || configModule;
92
+
93
+ if (typeof config === "function") {
94
+ return await config();
95
+ }
96
+
97
+ return config;
98
+ } finally {
99
+ await fs.unlink(tempFile).catch(() => {});
100
+ }
101
+ } catch (error) {
102
+ console.error(
103
+ `❌ Failed to load TypeScript config from ${configPath}:`,
104
+ error
105
+ );
106
+ throw error;
107
+ }
108
+ }
109
+
110
+ export function getDefaultConfig(): PodConfig {
111
+ return {
112
+ name: "app",
113
+ build: {
114
+ outDir: "dist",
115
+ sourcemap: true,
116
+ minify: false,
117
+ },
118
+ plugins: [],
119
+ client_plugins: [],
120
+ server_plugins: [],
121
+ };
122
+ }
123
+
124
+ export function mergeConfig(
125
+ defaults: PodConfig,
126
+ userConfig: PodConfig
127
+ ): PodConfig {
128
+ return {
129
+ name: userConfig.name,
130
+ build: { ...defaults.build, ...userConfig.build },
131
+ plugins: [...(defaults.plugins || []), ...(userConfig.plugins || [])],
132
+ client_plugins: [
133
+ ...(defaults.client_plugins || []),
134
+ ...(userConfig.client_plugins || []),
135
+ ],
136
+ server_plugins: [
137
+ ...(defaults.server_plugins || []),
138
+ ...(userConfig.server_plugins || []),
139
+ ],
140
+ };
141
+ }
@@ -0,0 +1 @@
1
+ export * from "./config"
@@ -0,0 +1 @@
1
+ export * from "./server";
@@ -0,0 +1,45 @@
1
+ import * as ts from "typescript";
2
+
3
+ export class Project {
4
+ public program: ts.Program;
5
+ public checker: ts.TypeChecker;
6
+
7
+ constructor(entryPoints: string[]) {
8
+ this.program = ts.createProgram(entryPoints, {
9
+ target: ts.ScriptTarget.ESNext,
10
+ module: ts.ModuleKind.ESNext,
11
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
12
+ allowJs: true,
13
+ jsx: ts.JsxEmit.ReactJSX,
14
+ });
15
+ this.checker = this.program.getTypeChecker();
16
+ }
17
+
18
+ isPublicFile(sourceFile: ts.SourceFile): boolean {
19
+ for (const stmt of sourceFile.statements) {
20
+ if (
21
+ ts.isExpressionStatement(stmt) &&
22
+ ts.isStringLiteral(stmt.expression)
23
+ ) {
24
+ if (stmt.expression.text === "use public") return true;
25
+ } else {
26
+ break;
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+
32
+ isInteractiveFile(sourceFile: ts.SourceFile): boolean {
33
+ for (const stmt of sourceFile.statements) {
34
+ if (
35
+ ts.isExpressionStatement(stmt) &&
36
+ ts.isStringLiteral(stmt.expression)
37
+ ) {
38
+ if (stmt.expression.text === "use interactive") return true;
39
+ } else {
40
+ break;
41
+ }
42
+ }
43
+ return false;
44
+ }
45
+ }
@@ -0,0 +1,190 @@
1
+ import * as esbuild from "esbuild";
2
+ import { spawn, ChildProcess } from "child_process";
3
+ import * as fs from "fs/promises";
4
+ import { loadConfig, mergeConfig, getDefaultConfig } from "../config/config";
5
+ import { buildGraph, useMyPlugin } from "@/plugins";
6
+ import { Store } from "@/store";
7
+
8
+ async function copyFile(): Promise<void> {
9
+ try {
10
+ await fs.mkdir("public", { recursive: true });
11
+ await fs.copyFile("./src/client/index.html", "./public/index.html");
12
+ } catch (error) {
13
+ console.error("❌ Failed to copy index.html:", error);
14
+ throw error;
15
+ }
16
+ }
17
+
18
+ async function cleanDirectories(): Promise<void> {
19
+ await Promise.all([
20
+ fs.rm("dist", { recursive: true, force: true }),
21
+ fs.rm("public", { recursive: true, force: true }),
22
+ ]);
23
+ }
24
+
25
+ function createRestartServerPlugin(
26
+ serverProcess: { current: ChildProcess | null },
27
+ onServerBuildComplete: () => void
28
+ ): esbuild.Plugin {
29
+ return {
30
+ name: "restart-server",
31
+ setup(build) {
32
+ build.onEnd((result) => {
33
+ if (result.errors.length > 0) {
34
+ console.error(
35
+ `❌ Server build failed with ${result.errors.length} error(s)`
36
+ );
37
+ return;
38
+ }
39
+
40
+ if (serverProcess.current) {
41
+ serverProcess.current.kill("SIGTERM");
42
+ }
43
+
44
+ serverProcess.current = spawn("node", ["dist/main.js"], {
45
+ stdio: "inherit",
46
+ });
47
+
48
+ serverProcess.current.on("error", (err) => {
49
+ console.error("❌ Server process error:", err);
50
+ });
51
+
52
+ onServerBuildComplete();
53
+ });
54
+ },
55
+ };
56
+ }
57
+
58
+ export async function startDevServer(): Promise<void> {
59
+ const store = Store.getInstance();
60
+ const userConfig = await loadConfig();
61
+ const config = mergeConfig(getDefaultConfig(), userConfig);
62
+
63
+ await cleanDirectories();
64
+ await copyFile();
65
+
66
+ const entryPoints = ["src/main.ts"];
67
+ const clientFiles = new Set<string>(["src/client/client.tsx"]);
68
+ const serverProcessRef = { current: null as ChildProcess | null };
69
+ let clientCtx: esbuild.BuildContext | null = null;
70
+ let isShuttingDown = false;
71
+
72
+ let pendingClientFiles = new Set<string>();
73
+ let needsClientRebuild = false;
74
+
75
+ async function rebuildClient(): Promise<void> {
76
+ if (isShuttingDown) return;
77
+
78
+ try {
79
+ if (clientCtx) {
80
+ await clientCtx.dispose();
81
+ clientCtx = null;
82
+ }
83
+
84
+ if (clientFiles.size === 0) return;
85
+
86
+ const entryPoints = Array.from(clientFiles);
87
+ const graph = buildGraph(entryPoints);
88
+
89
+ clientCtx = await esbuild.context({
90
+ entryPoints,
91
+ bundle: true,
92
+ outdir: "public",
93
+ outbase: ".",
94
+ platform: "browser",
95
+ format: "esm",
96
+ sourcemap: config.build?.sourcemap ?? true,
97
+ splitting: true,
98
+ minify: config.build?.minify ?? false,
99
+ plugins: [
100
+ ...(config.plugins?.map((cb) => cb(store)) || []),
101
+ ...(config.client_plugins?.map((cb) => cb(store)) || []),
102
+ useMyPlugin({
103
+ graph,
104
+ isServerBuild: false,
105
+ onClientFound: () => {},
106
+ }),
107
+ {
108
+ name: "client-build-logger",
109
+ setup(build: any) {
110
+ build.onEnd((result: any) => {
111
+ if (result.errors.length > 0) {
112
+ console.error(
113
+ `❌ Client build failed with ${result.errors.length} error(s)`
114
+ );
115
+ }
116
+ });
117
+ },
118
+ },
119
+ ],
120
+ write: true,
121
+ });
122
+
123
+ await clientCtx.watch();
124
+ pendingClientFiles.clear();
125
+ needsClientRebuild = false;
126
+ } catch (error) {
127
+ console.error("❌ Failed to rebuild client:", error);
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ async function onServerBuildComplete(): Promise<void> {
133
+ if (needsClientRebuild && pendingClientFiles.size > 0) {
134
+ await rebuildClient();
135
+ }
136
+ }
137
+
138
+ const serverCtx = await esbuild.context({
139
+ entryPoints,
140
+ bundle: true,
141
+ outdir: config.build?.outDir || "dist",
142
+ platform: "node",
143
+ format: "esm",
144
+ packages: "external",
145
+ sourcemap: config.build?.sourcemap ?? true,
146
+ minify: config.build?.minify ?? false,
147
+ plugins: [
148
+ ...(config.plugins?.map((cb) => cb(store)) || []),
149
+ useMyPlugin({
150
+ isServerBuild: true,
151
+ onClientFound: async (filePath) => {
152
+ const isNewFile = !clientFiles.has(filePath);
153
+
154
+ if (isNewFile) {
155
+ clientFiles.add(filePath);
156
+ pendingClientFiles.add(filePath);
157
+ needsClientRebuild = true;
158
+ }
159
+ },
160
+ }),
161
+ createRestartServerPlugin(serverProcessRef, onServerBuildComplete),
162
+ ],
163
+ write: true,
164
+ });
165
+
166
+ async function shutdown(): Promise<void> {
167
+ if (isShuttingDown) return;
168
+ isShuttingDown = true;
169
+
170
+ try {
171
+ if (serverProcessRef.current) {
172
+ serverProcessRef.current.kill("SIGTERM");
173
+ await new Promise((resolve) => setTimeout(resolve, 1000));
174
+ }
175
+
176
+ await serverCtx.dispose();
177
+ if (clientCtx) await clientCtx.dispose();
178
+
179
+ process.exit(0);
180
+ } catch (error) {
181
+ console.error("❌ Error during shutdown:", error);
182
+ process.exit(1);
183
+ }
184
+ }
185
+
186
+ process.on("SIGINT", shutdown);
187
+ process.on("SIGTERM", shutdown);
188
+
189
+ await serverCtx.watch();
190
+ }