create-caspian-app 0.0.19 → 0.0.20

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
@@ -352,11 +352,11 @@ npx create-caspian-app
352
352
 
353
353
  ### Useful flags
354
354
 
355
+ - `--backend-only` skip frontend assets
355
356
  - `--tailwindcss` Tailwind CSS v4 + PostCSS + `globals.css`
356
357
  - `--typescript` TypeScript support with Vite + `tsconfig.json`
357
- - `--websocket` WebSocket server scaffolding
358
358
  - `--mcp` Model Context Protocol server scaffolding (AI Agents)
359
- - `--backend-only` skip frontend assets
359
+ - `--prisma` Prisma ORM integration + sample schema
360
360
 
361
361
  ### Code generation
362
362
 
@@ -29,10 +29,12 @@ export function generateGlobalTypes(): Plugin {
29
29
  },
30
30
  };
31
31
  }
32
+
32
33
  interface GlobalDeclaration {
33
34
  name: string;
34
35
  importPath: string;
35
36
  exportName: string;
37
+ isNamespace: boolean;
36
38
  }
37
39
 
38
40
  function parseGlobalSingletons(
@@ -47,7 +49,10 @@ function parseGlobalSingletons(
47
49
  );
48
50
 
49
51
  const globals: GlobalDeclaration[] = [];
50
- const importMap = new Map<string, { path: string; originalName: string }>();
52
+ const importMap = new Map<
53
+ string,
54
+ { path: string; originalName: string; isNamespace: boolean }
55
+ >();
51
56
 
52
57
  sf.statements.forEach((stmt) => {
53
58
  if (ts.isImportDeclaration(stmt) && stmt.importClause) {
@@ -64,9 +69,24 @@ function parseGlobalSingletons(
64
69
  importMap.set(localName, {
65
70
  path: moduleSpecifier,
66
71
  originalName: importedName,
72
+ isNamespace: false,
67
73
  });
68
74
  });
75
+ } else if (ts.isNamespaceImport(stmt.importClause.namedBindings)) {
76
+ const localName = stmt.importClause.namedBindings.name.text;
77
+ importMap.set(localName, {
78
+ path: moduleSpecifier,
79
+ originalName: localName,
80
+ isNamespace: true,
81
+ });
69
82
  }
83
+ } else if (stmt.importClause.name) {
84
+ const localName = stmt.importClause.name.text;
85
+ importMap.set(localName, {
86
+ path: moduleSpecifier,
87
+ originalName: "default",
88
+ isNamespace: false,
89
+ });
70
90
  }
71
91
  }
72
92
  });
@@ -91,12 +111,12 @@ function parseGlobalSingletons(
91
111
  name,
92
112
  importPath: importInfo.path,
93
113
  exportName: importInfo.originalName,
114
+ isNamespace: importInfo.isNamespace,
94
115
  });
95
116
  }
96
117
  }
97
118
  }
98
119
  }
99
-
100
120
  ts.forEachChild(node, visit);
101
121
  }
102
122
 
@@ -114,82 +134,121 @@ function generateDtsWithTypeChecker(
114
134
  ts.sys.fileExists,
115
135
  "tsconfig.json"
116
136
  );
117
-
118
137
  const { config } = configPath
119
138
  ? ts.readConfigFile(configPath, ts.sys.readFile)
120
139
  : { config: {} };
121
-
122
- const { options } = ts.parseJsonConfigFileContent(
140
+ const parsedConfig = ts.parseJsonConfigFileContent(
123
141
  config,
124
142
  ts.sys,
125
143
  process.cwd()
126
144
  );
127
145
 
128
- const program = ts.createProgram([mainPath], options);
146
+ const program = ts.createProgram(
147
+ parsedConfig.fileNames,
148
+ parsedConfig.options
149
+ );
129
150
  const checker = program.getTypeChecker();
130
151
  const sourceFile = program.getSourceFile(mainPath);
131
152
 
132
153
  if (!sourceFile) {
133
- console.warn("⚠️ Could not load main.ts for type checking");
134
154
  generateFallbackDts(globals, dtsPath);
135
155
  return;
136
156
  }
137
157
 
138
158
  const signatures = new Map<string, string>();
139
-
140
159
  const importMap = new Map<string, ts.ImportDeclaration>();
160
+
141
161
  sourceFile.statements.forEach((stmt) => {
142
- if (ts.isImportDeclaration(stmt) && stmt.importClause?.namedBindings) {
143
- if (ts.isNamedImports(stmt.importClause.namedBindings)) {
144
- stmt.importClause.namedBindings.elements.forEach((element) => {
145
- importMap.set(element.name.text, stmt);
146
- });
162
+ if (ts.isImportDeclaration(stmt)) {
163
+ if (stmt.importClause?.namedBindings) {
164
+ if (ts.isNamedImports(stmt.importClause.namedBindings)) {
165
+ stmt.importClause.namedBindings.elements.forEach((element) => {
166
+ importMap.set(element.name.text, stmt);
167
+ });
168
+ } else if (ts.isNamespaceImport(stmt.importClause.namedBindings)) {
169
+ importMap.set(stmt.importClause.namedBindings.name.text, stmt);
170
+ }
171
+ } else if (stmt.importClause?.name) {
172
+ importMap.set(stmt.importClause.name.text, stmt);
147
173
  }
148
174
  }
149
175
  });
150
176
 
151
- globals.forEach(({ name, exportName }) => {
152
- try {
153
- const importDecl = importMap.get(exportName);
154
- if (!importDecl || !importDecl.importClause?.namedBindings) {
155
- signatures.set(name, "(...args: any[]) => any");
156
- return;
157
- }
177
+ globals.forEach(({ name, exportName, importPath, isNamespace }) => {
178
+ // RESOLVE SIGNATURE
179
+ const isExternalLibrary =
180
+ !importPath.startsWith(".") && !importPath.startsWith("/");
158
181
 
159
- if (ts.isNamedImports(importDecl.importClause.namedBindings)) {
160
- const importSpec = importDecl.importClause.namedBindings.elements.find(
161
- (el) => el.name.text === exportName
162
- );
182
+ if (isExternalLibrary) {
183
+ if (isNamespace) {
184
+ signatures.set(name, `typeof import("${importPath}")`);
185
+ } else {
186
+ signatures.set(name, `typeof import("${importPath}").${exportName}`);
187
+ }
188
+ return;
189
+ }
163
190
 
164
- if (importSpec) {
165
- const symbol = checker.getSymbolAtLocation(importSpec.name);
166
- if (symbol) {
167
- const type = checker.getTypeOfSymbolAtLocation(
168
- symbol,
169
- importSpec.name
170
- );
171
- const signature = checker.typeToString(
172
- type,
173
- undefined,
174
- ts.TypeFormatFlags.NoTruncation
191
+ try {
192
+ const importDecl =
193
+ importMap.get(exportName === "default" ? name : exportName) ||
194
+ importMap.get(isNamespace ? name : exportName);
195
+ let symbol: ts.Symbol | undefined;
196
+
197
+ if (importDecl && importDecl.importClause) {
198
+ if (importDecl.importClause.namedBindings) {
199
+ if (ts.isNamedImports(importDecl.importClause.namedBindings)) {
200
+ const importSpec =
201
+ importDecl.importClause.namedBindings.elements.find(
202
+ (el) =>
203
+ (el.propertyName?.text || el.name.text) === exportName ||
204
+ el.name.text === exportName
205
+ );
206
+ if (importSpec)
207
+ symbol = checker.getSymbolAtLocation(importSpec.name);
208
+ } else if (
209
+ ts.isNamespaceImport(importDecl.importClause.namedBindings)
210
+ ) {
211
+ symbol = checker.getSymbolAtLocation(
212
+ importDecl.importClause.namedBindings.name
175
213
  );
176
- signatures.set(name, signature);
177
- return;
178
214
  }
215
+ } else if (importDecl.importClause.name) {
216
+ symbol = checker.getSymbolAtLocation(importDecl.importClause.name);
179
217
  }
180
218
  }
181
219
 
182
- signatures.set(name, "(...args: any[]) => any");
220
+ if (symbol) {
221
+ const aliasedSymbol = checker.getAliasedSymbol(symbol);
222
+ const targetSymbol = aliasedSymbol || symbol;
223
+ const type = checker.getTypeOfSymbolAtLocation(
224
+ targetSymbol,
225
+ targetSymbol.valueDeclaration!
226
+ );
227
+ const signature = checker.typeToString(
228
+ type,
229
+ undefined,
230
+ ts.TypeFormatFlags.NoTruncation |
231
+ ts.TypeFormatFlags.UseFullyQualifiedType
232
+ );
233
+
234
+ if (signature !== "any") {
235
+ signatures.set(name, signature);
236
+ return;
237
+ }
238
+ }
183
239
  } catch (error) {
184
- console.warn(`⚠️ Failed to extract type for ${name}:`, error);
185
- signatures.set(name, "(...args: any[]) => any");
240
+ console.warn(`Failed to resolve type for ${name}`);
186
241
  }
242
+
243
+ // Fallback
244
+ signatures.set(name, "any");
187
245
  });
188
246
 
189
247
  const declarations = globals
190
- .map(({ name }) => {
191
- const sig = signatures.get(name) || "(...args: any[]) => any";
192
- return ` const ${name}: ${sig};`;
248
+ .map(({ name, importPath }) => {
249
+ const sig = signatures.get(name) || "any";
250
+ // FIX: Inject the source path as a comment for the Extension to read
251
+ return ` // @source: ${importPath}\n const ${name}: ${sig};`;
193
252
  })
194
253
  .join("\n");
195
254
 
@@ -213,17 +272,17 @@ export {};
213
272
  `;
214
273
 
215
274
  const dir = path.dirname(dtsPath);
216
- if (!existsSync(dir)) {
217
- mkdirSync(dir, { recursive: true });
218
- }
219
-
275
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
220
276
  writeFileSync(dtsPath, content, "utf-8");
221
277
  console.log(`✅ Generated ${path.relative(process.cwd(), dtsPath)}`);
222
278
  }
223
279
 
224
280
  function generateFallbackDts(globals: GlobalDeclaration[], dtsPath: string) {
225
281
  const declarations = globals
226
- .map(({ name }) => ` const ${name}: (...args: any[]) => any;`)
282
+ .map(
283
+ ({ name, importPath }) =>
284
+ ` // @source: ${importPath}\n const ${name}: any;`
285
+ )
227
286
  .join("\n");
228
287
 
229
288
  const windowDeclarations = globals
@@ -238,9 +297,7 @@ ${declarations}
238
297
  ${windowDeclarations}
239
298
  }
240
299
  }
241
-
242
300
  export {};
243
301
  `;
244
-
245
302
  writeFileSync(dtsPath, content, "utf-8");
246
303
  }
@@ -36,6 +36,7 @@ export default defineConfig(({ command, mode }) => ({
36
36
  emptyOutDir: false,
37
37
  minify: "esbuild",
38
38
  sourcemap: false,
39
+ chunkSizeWarningLimit: 1000,
39
40
  watch:
40
41
  command === "build" && mode === "development"
41
42
  ? { exclude: VITE_WATCH_EXCLUDE }
@@ -47,6 +48,15 @@ export default defineConfig(({ command, mode }) => ({
47
48
  entryFileNames: "[name].js",
48
49
  chunkFileNames: "chunks/[name]-[hash].js",
49
50
  assetFileNames: "assets/[name]-[hash][extname]",
51
+ manualChunks(id) {
52
+ if (id.includes("node_modules")) {
53
+ return id
54
+ .toString()
55
+ .split("node_modules/")[1]
56
+ .split("/")[0]
57
+ .toString();
58
+ }
59
+ },
50
60
  },
51
61
  },
52
62
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-caspian-app",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",