create-fullstack-scaffold 0.4.21 → 0.4.23
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/dist/cli/index.js
CHANGED
|
@@ -204,11 +204,13 @@ function getExcludePatterns(resolved, allManifests) {
|
|
|
204
204
|
if (!resolved.modules.has("tenant")) {
|
|
205
205
|
excludes.push("src/tenant");
|
|
206
206
|
excludes.push("tenant.html");
|
|
207
|
-
excludes.push("src/merchant");
|
|
208
|
-
excludes.push("merchant.html");
|
|
209
207
|
excludes.push("src/server/middleware/tenant-isolation.ts");
|
|
210
208
|
excludes.push("src/server/middleware/__tests__/tenant-isolation.test.ts");
|
|
211
209
|
}
|
|
210
|
+
if (!resolved.modules.has("merchant")) {
|
|
211
|
+
excludes.push("src/merchant");
|
|
212
|
+
excludes.push("merchant.html");
|
|
213
|
+
}
|
|
212
214
|
if (!resolved.hasClient) {
|
|
213
215
|
excludes.push("src/client");
|
|
214
216
|
excludes.push("index.html");
|
|
@@ -229,15 +231,16 @@ function getExcludePatterns(resolved, allManifests) {
|
|
|
229
231
|
}
|
|
230
232
|
excludes.push("src/client/preset-ui-config.ts");
|
|
231
233
|
const standaloneSharedModules = {
|
|
232
|
-
cart: ["CartPage"],
|
|
233
|
-
community: ["TopicsPage"],
|
|
234
|
-
dashboard: ["DashboardPage"]
|
|
234
|
+
cart: { pages: ["CartPage"] },
|
|
235
|
+
community: { pages: ["TopicsPage", "ProfilePage"], serverModules: ["content"] },
|
|
236
|
+
dashboard: { pages: ["DashboardPage"] }
|
|
235
237
|
};
|
|
236
|
-
for (const [moduleName,
|
|
237
|
-
const hasRelevantPage = [...resolved.modules.values()].some(
|
|
238
|
-
(m) => m.clientPages?.some((p) =>
|
|
238
|
+
for (const [moduleName, config] of Object.entries(standaloneSharedModules)) {
|
|
239
|
+
const hasRelevantPage = config.pages && [...resolved.modules.values()].some(
|
|
240
|
+
(m) => m.clientPages?.some((p) => config.pages.includes(p.name)) ?? false
|
|
239
241
|
);
|
|
240
|
-
|
|
242
|
+
const hasRelevantServerModule = config.serverModules?.some((sm) => resolved.modules.has(sm)) ?? false;
|
|
243
|
+
if (!hasRelevantPage && !hasRelevantServerModule) {
|
|
241
244
|
excludes.push(`src/shared/modules/${moduleName}`);
|
|
242
245
|
}
|
|
243
246
|
}
|
|
@@ -261,6 +264,10 @@ function getExcludePatterns(resolved, allManifests) {
|
|
|
261
264
|
}
|
|
262
265
|
if (!resolved.modules.has("captcha")) {
|
|
263
266
|
excludes.push("src/server/utils/__tests__/captcha.test.ts");
|
|
267
|
+
excludes.push("src/admin/components/CaptchaModal.tsx");
|
|
268
|
+
excludes.push("src/admin/stores/captchaStore.ts");
|
|
269
|
+
excludes.push("src/admin/stores/__tests__/captchaStore.test.ts");
|
|
270
|
+
excludes.push("src/admin/stores/__tests__/captchaStoreBranches.test.ts");
|
|
264
271
|
}
|
|
265
272
|
return excludes;
|
|
266
273
|
}
|
|
@@ -288,6 +295,8 @@ function getGeneratedFiles(resolved) {
|
|
|
288
295
|
}
|
|
289
296
|
if (resolved.hasClient && resolved.modules.has("admin")) {
|
|
290
297
|
files.push("src/admin/App.tsx");
|
|
298
|
+
files.push("src/admin/components/index.ts");
|
|
299
|
+
files.push("src/admin/services/apiClient.ts");
|
|
291
300
|
}
|
|
292
301
|
if (resolved.hasClient && !resolved.modules.has("admin")) {
|
|
293
302
|
files.push("vite.config.ts");
|
|
@@ -727,6 +736,93 @@ ${protectedRouteElements.join("\n")}
|
|
|
727
736
|
}
|
|
728
737
|
`;
|
|
729
738
|
}
|
|
739
|
+
function generateAdminComponentsIndex(resolved) {
|
|
740
|
+
if (!resolved.hasAdmin) return null;
|
|
741
|
+
const lines = [
|
|
742
|
+
`export { UserTable } from './UserTable'`,
|
|
743
|
+
`export { StatsCard } from './StatsCard'`,
|
|
744
|
+
`export { PageHeader } from './PageHeader'`,
|
|
745
|
+
`export { UserFormModal } from './UserFormModal'`,
|
|
746
|
+
`export { ProtectedRoute } from './ProtectedRoute'`
|
|
747
|
+
];
|
|
748
|
+
if (resolved.hasCaptcha) {
|
|
749
|
+
lines.push(`export { CaptchaModal } from './CaptchaModal'`);
|
|
750
|
+
}
|
|
751
|
+
lines.push(`export { PermissionGuard, PermissionButton, Can, Cannot } from './PermissionGuard'`);
|
|
752
|
+
return lines.join("\n") + "\n";
|
|
753
|
+
}
|
|
754
|
+
function generateAdminApiClient(resolved) {
|
|
755
|
+
if (!resolved.hasAdmin) return null;
|
|
756
|
+
const captchaImport = resolved.hasCaptcha ? `import { useCaptchaStore } from '../stores/captchaStore'
|
|
757
|
+
` : "";
|
|
758
|
+
const captchaFetchSetup = resolved.hasCaptcha ? ` const showCaptcha = useCaptchaStore.getState().show
|
|
759
|
+
|
|
760
|
+
` : "";
|
|
761
|
+
const captchaHandler = resolved.hasCaptcha ? ` onShowCaptcha: async config => {
|
|
762
|
+
return showCaptcha({
|
|
763
|
+
type: config.type,
|
|
764
|
+
captchaUrl: config.captchaUrl,
|
|
765
|
+
})
|
|
766
|
+
},` : ` onShowCaptcha: async () => true,`;
|
|
767
|
+
return `/**
|
|
768
|
+
* @framework-baseline ab16e97716a7556e
|
|
769
|
+
* @framework-modify
|
|
770
|
+
* @reason Conditional captcha support based on preset modules
|
|
771
|
+
* @impact Captcha module excluded = no captchaStore import
|
|
772
|
+
*/
|
|
773
|
+
|
|
774
|
+
import { hc } from 'hono/client'
|
|
775
|
+
import { WSClientImpl } from '@shared/core/ws-client'
|
|
776
|
+
import { SSEClientImpl } from '@shared/core/sse-client'
|
|
777
|
+
import { createRequestInterceptor } from './requestInterceptor'
|
|
778
|
+
${captchaImport}import type { AdminApiType } from '@server/index'
|
|
779
|
+
|
|
780
|
+
const baseUrl = import.meta.env.API_BASE_URL || window.location.origin
|
|
781
|
+
|
|
782
|
+
const TOKEN_KEY = 'admin-storage'
|
|
783
|
+
|
|
784
|
+
function clearAuthAndRedirect(): void {
|
|
785
|
+
localStorage.removeItem(TOKEN_KEY)
|
|
786
|
+
if (window.location.pathname !== '/admin/login') {
|
|
787
|
+
window.location.href = '/admin/login'
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function getAuthToken(): string | null {
|
|
792
|
+
try {
|
|
793
|
+
const stored = localStorage.getItem(TOKEN_KEY)
|
|
794
|
+
if (stored) {
|
|
795
|
+
const parsed = JSON.parse(stored)
|
|
796
|
+
return parsed.state?.token || null
|
|
797
|
+
}
|
|
798
|
+
} catch {
|
|
799
|
+
return null
|
|
800
|
+
}
|
|
801
|
+
return null
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function createCustomFetch() {
|
|
805
|
+
${captchaFetchSetup} return createRequestInterceptor({
|
|
806
|
+
onShowLogin: clearAuthAndRedirect,
|
|
807
|
+
${captchaHandler} })
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export const apiClient = hc<AdminApiType>(baseUrl, {
|
|
811
|
+
fetch: createCustomFetch() as typeof fetch,
|
|
812
|
+
webSocket: url => new WSClientImpl(url) as unknown as WebSocket,
|
|
813
|
+
sse: url => {
|
|
814
|
+
const token = getAuthToken()
|
|
815
|
+
const headers: Record<string, string> = {}
|
|
816
|
+
if (token) {
|
|
817
|
+
headers['Authorization'] = \`Bearer \${token}\`
|
|
818
|
+
}
|
|
819
|
+
return new SSEClientImpl(url, headers)
|
|
820
|
+
},
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
export { api } from '@shared/core/api-request'
|
|
824
|
+
`;
|
|
825
|
+
}
|
|
730
826
|
|
|
731
827
|
// src/generators/db-schema-barrel.ts
|
|
732
828
|
function generateDbSchemaBarrel(resolved) {
|
|
@@ -1686,9 +1782,9 @@ function generateSharedModulesIndex(resolved) {
|
|
|
1686
1782
|
"captcha"
|
|
1687
1783
|
];
|
|
1688
1784
|
const STANDALONE_SHARED_MODULES2 = {
|
|
1689
|
-
cart: ["CartPage.tsx"],
|
|
1690
|
-
community: ["TopicsPage.tsx"],
|
|
1691
|
-
dashboard: ["DashboardPage.tsx"]
|
|
1785
|
+
cart: { pages: ["CartPage.tsx"] },
|
|
1786
|
+
community: { pages: ["TopicsPage.tsx", "ProfilePage.tsx"], serverModules: ["content"] },
|
|
1787
|
+
dashboard: { pages: ["DashboardPage.tsx"] }
|
|
1692
1788
|
};
|
|
1693
1789
|
for (const moduleName of moduleOrder2) {
|
|
1694
1790
|
if (!resolved.modules.has(moduleName)) continue;
|
|
@@ -1716,11 +1812,12 @@ function generateSharedModulesIndex(resolved) {
|
|
|
1716
1812
|
}
|
|
1717
1813
|
}
|
|
1718
1814
|
}
|
|
1719
|
-
for (const [moduleName,
|
|
1720
|
-
const hasRelevantPage = [...resolved.modules.values()].some(
|
|
1721
|
-
(m) => m.clientPages?.some((p) =>
|
|
1815
|
+
for (const [moduleName, config] of Object.entries(STANDALONE_SHARED_MODULES2)) {
|
|
1816
|
+
const hasRelevantPage = config.pages && [...resolved.modules.values()].some(
|
|
1817
|
+
(m) => m.clientPages?.some((p) => config.pages.includes(p.name + ".tsx")) ?? false
|
|
1722
1818
|
);
|
|
1723
|
-
|
|
1819
|
+
const hasRelevantServerModule = config.serverModules?.some((sm) => resolved.modules.has(sm)) ?? false;
|
|
1820
|
+
if (!hasRelevantPage && !hasRelevantServerModule) continue;
|
|
1724
1821
|
const exports = MODULE_EXPORTS[moduleName];
|
|
1725
1822
|
if (!exports) continue;
|
|
1726
1823
|
lines.push(`export {
|
|
@@ -2148,9 +2245,9 @@ var ADDITIONAL_PATHS_MAP = {
|
|
|
2148
2245
|
permission: ["role", "audit"]
|
|
2149
2246
|
};
|
|
2150
2247
|
var STANDALONE_SHARED_MODULES = {
|
|
2151
|
-
cart: ["CartPage.tsx"],
|
|
2152
|
-
community: ["TopicsPage.tsx"],
|
|
2153
|
-
dashboard: ["DashboardPage.tsx"]
|
|
2248
|
+
cart: { pages: ["CartPage.tsx"] },
|
|
2249
|
+
community: { pages: ["TopicsPage.tsx", "ProfilePage.tsx"], serverModules: ["content"] },
|
|
2250
|
+
dashboard: { pages: ["DashboardPage.tsx"] }
|
|
2154
2251
|
};
|
|
2155
2252
|
var moduleOrder = [
|
|
2156
2253
|
"chat",
|
|
@@ -2234,12 +2331,13 @@ export {
|
|
|
2234
2331
|
}
|
|
2235
2332
|
function shouldIncludeModule(moduleName, resolved) {
|
|
2236
2333
|
if (resolved.modules.has(moduleName)) return true;
|
|
2237
|
-
const
|
|
2238
|
-
if (
|
|
2239
|
-
const hasRelevantPage = [...resolved.modules.values()].some(
|
|
2240
|
-
(m) => m.clientPages?.some((p) =>
|
|
2334
|
+
const standalone = STANDALONE_SHARED_MODULES[moduleName];
|
|
2335
|
+
if (standalone) {
|
|
2336
|
+
const hasRelevantPage = standalone.pages && [...resolved.modules.values()].some(
|
|
2337
|
+
(m) => m.clientPages?.some((p) => standalone.pages.includes(p.name + ".tsx")) ?? false
|
|
2241
2338
|
);
|
|
2242
|
-
|
|
2339
|
+
const hasRelevantServerModule = standalone.serverModules?.some((sm) => resolved.modules.has(sm)) ?? false;
|
|
2340
|
+
return !!(hasRelevantPage || hasRelevantServerModule);
|
|
2243
2341
|
}
|
|
2244
2342
|
for (const [parentModule, additionalPaths] of Object.entries(ADDITIONAL_PATHS_MAP)) {
|
|
2245
2343
|
if (additionalPaths.includes(moduleName) && resolved.modules.has(parentModule)) {
|
|
@@ -2680,7 +2778,8 @@ function filterPackageJson(pkg, resolved) {
|
|
|
2680
2778
|
packagesToRemove.add(pkg2);
|
|
2681
2779
|
}
|
|
2682
2780
|
}
|
|
2683
|
-
|
|
2781
|
+
const hasAntdConsumer = resolved.modules.has("admin") || resolved.modules.has("tenant") || resolved.modules.has("merchant");
|
|
2782
|
+
if (!hasAntdConsumer) {
|
|
2684
2783
|
for (const pkg2 of ADMIN_PANEL_PACKAGES) {
|
|
2685
2784
|
packagesToRemove.add(pkg2);
|
|
2686
2785
|
}
|
|
@@ -2749,8 +2848,12 @@ function generateViteConfig(resolved, templateDir) {
|
|
|
2749
2848
|
aliasesToRemove.push("@admin");
|
|
2750
2849
|
}
|
|
2751
2850
|
if (!resolved.modules.has("tenant")) {
|
|
2752
|
-
entriesToRemove.push("tenant"
|
|
2753
|
-
aliasesToRemove.push("@tenant"
|
|
2851
|
+
entriesToRemove.push("tenant");
|
|
2852
|
+
aliasesToRemove.push("@tenant");
|
|
2853
|
+
}
|
|
2854
|
+
if (!resolved.modules.has("merchant")) {
|
|
2855
|
+
entriesToRemove.push("merchant");
|
|
2856
|
+
aliasesToRemove.push("@merchant");
|
|
2754
2857
|
}
|
|
2755
2858
|
for (const name of entriesToRemove) {
|
|
2756
2859
|
const re = new RegExp(
|
|
@@ -3756,6 +3859,19 @@ async function createProject(projectNameOrOptions, useCurrentDir = false, preset
|
|
|
3756
3859
|
await fs.ensureDir(path.join(targetDir, "src/admin"));
|
|
3757
3860
|
await fs.writeFile(path.join(targetDir, "src/admin/App.tsx"), adminAppContent);
|
|
3758
3861
|
}
|
|
3862
|
+
const adminComponentsIndex = generateAdminComponentsIndex(resolved);
|
|
3863
|
+
if (adminComponentsIndex) {
|
|
3864
|
+
await fs.ensureDir(path.join(targetDir, "src/admin/components"));
|
|
3865
|
+
await fs.writeFile(
|
|
3866
|
+
path.join(targetDir, "src/admin/components/index.ts"),
|
|
3867
|
+
adminComponentsIndex
|
|
3868
|
+
);
|
|
3869
|
+
}
|
|
3870
|
+
const adminApiClient = generateAdminApiClient(resolved);
|
|
3871
|
+
if (adminApiClient) {
|
|
3872
|
+
await fs.ensureDir(path.join(targetDir, "src/admin/services"));
|
|
3873
|
+
await fs.writeFile(path.join(targetDir, "src/admin/services/apiClient.ts"), adminApiClient);
|
|
3874
|
+
}
|
|
3759
3875
|
}
|
|
3760
3876
|
const serverAppContent = generateServerApp(resolved);
|
|
3761
3877
|
await fs.writeFile(path.join(targetDir, "src/server/app.ts"), serverAppContent);
|