@lark-apaas/fullstack-cli 0.1.0-alpha.8 → 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.
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Lark Technologies Pte. Ltd. and/or its affiliates
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
8
+ IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
9
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
10
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
11
+ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
12
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
13
+ ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/bin/cli.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'node:url';
4
+ import path from 'node:path';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ // 直接执行编译后的 index.js
10
+ const indexPath = path.join(__dirname, '../dist/index.js');
11
+ await import(indexPath);
@@ -1,10 +1,19 @@
1
1
  /**
2
2
  * 生成数据库 schema
3
3
  *
4
+ * 命令行选项:
5
+ * - --output <path>: schema 输出路径,默认 'server/database/schema.ts'
6
+ * - --schema-filter <schemas>: schema 过滤器,逗号分隔
7
+ * - --tables-filter <tables>: 表过滤器,逗号分隔,默认 '*'
8
+ *
4
9
  * 环境变量配置:
5
10
  * - SUDA_DATABASE_URL: 数据库连接 URL(必需)
6
- * - DB_SCHEMA_OUTPUT: schema 输出路径,默认 'server/database/schema.ts'
7
- * - DRIZZLE_SCHEMA_FILTER: schema 过滤器,逗号分隔
8
- * - DRIZZLE_TABLES_FILTER: 表过滤器,逗号分隔,默认 '*'
11
+ * - DB_SCHEMA_OUTPUT: schema 输出路径(命令行选项优先)
12
+ * - DRIZZLE_SCHEMA_FILTER: schema 过滤器(命令行选项优先)
13
+ * - DRIZZLE_TABLES_FILTER: 表过滤器(命令行选项优先)
9
14
  */
10
- export declare function run(): Promise<void>;
15
+ export declare function run(options?: {
16
+ output?: string;
17
+ schemaFilter?: string;
18
+ tablesFilter?: string;
19
+ }): Promise<void>;
@@ -2,19 +2,26 @@ import path from 'node:path';
2
2
  import fs from 'node:fs';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { spawnSync } from 'node:child_process';
5
+ import { createRequire } from 'node:module';
5
6
  // 加载 .env 配置
6
7
  import { config as loadEnv } from 'dotenv';
7
- import { postprocessDrizzleSchema } from '@lark-apaas/devtool-kits';
8
+ // 创建 require 函数来加载 CommonJS 模块
9
+ const require = createRequire(import.meta.url);
8
10
  /**
9
11
  * 生成数据库 schema
10
12
  *
13
+ * 命令行选项:
14
+ * - --output <path>: schema 输出路径,默认 'server/database/schema.ts'
15
+ * - --schema-filter <schemas>: schema 过滤器,逗号分隔
16
+ * - --tables-filter <tables>: 表过滤器,逗号分隔,默认 '*'
17
+ *
11
18
  * 环境变量配置:
12
19
  * - SUDA_DATABASE_URL: 数据库连接 URL(必需)
13
- * - DB_SCHEMA_OUTPUT: schema 输出路径,默认 'server/database/schema.ts'
14
- * - DRIZZLE_SCHEMA_FILTER: schema 过滤器,逗号分隔
15
- * - DRIZZLE_TABLES_FILTER: 表过滤器,逗号分隔,默认 '*'
20
+ * - DB_SCHEMA_OUTPUT: schema 输出路径(命令行选项优先)
21
+ * - DRIZZLE_SCHEMA_FILTER: schema 过滤器(命令行选项优先)
22
+ * - DRIZZLE_TABLES_FILTER: 表过滤器(命令行选项优先)
16
23
  */
17
- export async function run() {
24
+ export async function run(options = {}) {
18
25
  // 加载用户项目的 .env 文件
19
26
  const envPath = path.resolve(process.cwd(), '.env');
20
27
  if (fs.existsSync(envPath)) {
@@ -27,7 +34,8 @@ export async function run() {
27
34
  console.error('[gen-db-schema] Please set it in .env file or pass it as environment variable');
28
35
  process.exit(1);
29
36
  }
30
- const outputPath = process.env.DB_SCHEMA_OUTPUT || 'server/database/schema.ts';
37
+ // 命令行选项优先于环境变量
38
+ const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || 'server/database/schema.ts';
31
39
  const OUT_DIR = path.resolve(process.cwd(), 'server/database/.introspect');
32
40
  const SCHEMA_FILE = path.resolve(process.cwd(), outputPath);
33
41
  console.log('[gen-db-schema] Starting...');
@@ -36,8 +44,8 @@ export async function run() {
36
44
  const __filename = fileURLToPath(import.meta.url);
37
45
  const __dirname = path.dirname(__filename);
38
46
  // 使用 CLI 内部的 drizzle 配置
39
- const cliRoot = path.resolve(__dirname, '../..');
40
- const configPath = path.join(cliRoot, 'templates', 'drizzle.config.ts');
47
+ const cliRoot = path.resolve(__dirname, '../');
48
+ const configPath = path.join(cliRoot, 'config', 'drizzle.config.js');
41
49
  if (!fs.existsSync(configPath)) {
42
50
  console.error('[gen-db-schema] Error: drizzle config not found in CLI package');
43
51
  process.exit(1);
@@ -68,8 +76,9 @@ export async function run() {
68
76
  fs.mkdirSync(path.dirname(SCHEMA_FILE), { recursive: true });
69
77
  fs.copyFileSync(generatedSchema, SCHEMA_FILE);
70
78
  console.log(`[gen-db-schema] ✓ Copied to ${outputPath}`);
71
- // 后处理(如果 devtool-kits 可用)
79
+ // 后处理 schema(使用 CommonJS require 方式加载)
72
80
  try {
81
+ const { postprocessDrizzleSchema } = require('@lark-apaas/devtool-kits');
73
82
  const stats = postprocessDrizzleSchema(SCHEMA_FILE);
74
83
  if (stats?.unmatchedUnknown?.length) {
75
84
  console.warn('[gen-db-schema] Unmatched custom types detected:', stats.unmatchedUnknown);
@@ -77,7 +86,7 @@ export async function run() {
77
86
  console.log('[gen-db-schema] ✓ Postprocessed schema');
78
87
  }
79
88
  catch (error) {
80
- console.warn('[gen-db-schema] Postprocess skipped (devtool-kits not available)');
89
+ console.warn('[gen-db-schema] Postprocess failed:', error instanceof Error ? error.message : String(error));
81
90
  }
82
91
  // 清理临时文件
83
92
  if (fs.existsSync(OUT_DIR)) {
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 同步模板文件到用户项目
3
+ */
4
+ export declare function run(): Promise<void>;
@@ -0,0 +1,162 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { syncConfig } from '../config/sync.js';
5
+ /**
6
+ * 同步模板文件到用户项目
7
+ */
8
+ export async function run() {
9
+ // 检测是否在用户项目中(通过 INIT_CWD 判断)
10
+ const userProjectRoot = process.env.INIT_CWD || process.cwd();
11
+ // 获取插件根目录
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ const pluginRoot = path.resolve(__dirname, '../..');
15
+ // 只有在插件自己的开发目录中才跳过(避免在开发插件时触发 sync)
16
+ if (userProjectRoot === pluginRoot) {
17
+ console.log('[fullstack-cli] Skip syncing (installing plugin itself)');
18
+ process.exit(0);
19
+ }
20
+ // 检查是否存在 package.json,确保是有效的项目
21
+ const userPackageJson = path.join(userProjectRoot, 'package.json');
22
+ if (!fs.existsSync(userPackageJson)) {
23
+ console.log('[fullstack-cli] Skip syncing (not a valid npm project)');
24
+ process.exit(0);
25
+ }
26
+ try {
27
+ console.log('[fullstack-cli] Starting sync...');
28
+ // 使用配置
29
+ const config = syncConfig;
30
+ if (!config || !config.sync) {
31
+ console.warn('[fullstack-cli] No sync configuration found');
32
+ process.exit(0);
33
+ }
34
+ // 执行同步规则
35
+ for (const rule of config.sync) {
36
+ await syncRule(rule, pluginRoot, userProjectRoot);
37
+ }
38
+ // 设置权限
39
+ if (config.permissions) {
40
+ setPermissions(config.permissions, userProjectRoot);
41
+ }
42
+ console.log('[fullstack-cli] Sync completed successfully ✅');
43
+ }
44
+ catch (error) {
45
+ const message = error instanceof Error ? error.message : String(error);
46
+ console.error('[fullstack-cli] Failed to sync:', message);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ /**
51
+ * 执行单个同步规则
52
+ */
53
+ async function syncRule(rule, pluginRoot, userProjectRoot) {
54
+ const srcPath = path.join(pluginRoot, rule.from);
55
+ const destPath = path.join(userProjectRoot, rule.to);
56
+ if (!fs.existsSync(srcPath)) {
57
+ console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
58
+ return;
59
+ }
60
+ switch (rule.type) {
61
+ case 'directory':
62
+ syncDirectory(srcPath, destPath, rule.overwrite ?? true);
63
+ break;
64
+ case 'file':
65
+ syncFile(srcPath, destPath, rule.overwrite ?? true);
66
+ break;
67
+ case 'append':
68
+ appendToFile(srcPath, destPath);
69
+ break;
70
+ default:
71
+ console.warn(`[fullstack-cli] Unknown sync type: ${rule.type}`);
72
+ }
73
+ }
74
+ /**
75
+ * 同步单个文件
76
+ */
77
+ function syncFile(src, dest, overwrite = true) {
78
+ // 确保目标目录存在
79
+ const destDir = path.dirname(dest);
80
+ if (!fs.existsSync(destDir)) {
81
+ fs.mkdirSync(destDir, { recursive: true });
82
+ }
83
+ // 检查目标文件是否存在
84
+ if (fs.existsSync(dest) && !overwrite) {
85
+ console.log(`[fullstack-cli] ○ ${path.basename(dest)} (skipped, already exists)`);
86
+ return;
87
+ }
88
+ // 复制文件
89
+ fs.copyFileSync(src, dest);
90
+ console.log(`[fullstack-cli] ✓ ${path.basename(dest)}`);
91
+ }
92
+ /**
93
+ * 同步整个目录
94
+ */
95
+ function syncDirectory(src, dest, overwrite = true) {
96
+ // 确保目标目录存在
97
+ if (!fs.existsSync(dest)) {
98
+ fs.mkdirSync(dest, { recursive: true });
99
+ }
100
+ // 读取源目录所有文件
101
+ const files = fs.readdirSync(src);
102
+ let count = 0;
103
+ files.forEach(file => {
104
+ const srcFile = path.join(src, file);
105
+ const destFile = path.join(dest, file);
106
+ const stats = fs.statSync(srcFile);
107
+ if (stats.isDirectory()) {
108
+ // 递归处理子目录
109
+ syncDirectory(srcFile, destFile, overwrite);
110
+ }
111
+ else {
112
+ // 复制文件
113
+ if (overwrite || !fs.existsSync(destFile)) {
114
+ fs.copyFileSync(srcFile, destFile);
115
+ console.log(`[fullstack-cli] ✓ ${path.relative(dest, destFile)}`);
116
+ count++;
117
+ }
118
+ }
119
+ });
120
+ if (count > 0) {
121
+ console.log(`[fullstack-cli] Synced ${count} files to ${path.basename(dest)}/`);
122
+ }
123
+ }
124
+ /**
125
+ * 追加内容到文件
126
+ */
127
+ function appendToFile(src, dest) {
128
+ const content = fs.readFileSync(src, 'utf-8');
129
+ // 读取目标文件内容(如果存在)
130
+ let existingContent = '';
131
+ if (fs.existsSync(dest)) {
132
+ existingContent = fs.readFileSync(dest, 'utf-8');
133
+ }
134
+ // 检查是否已包含相同内容(避免重复追加)
135
+ if (existingContent.includes(content.trim())) {
136
+ console.log(`[fullstack-cli] ○ ${path.basename(dest)} (already contains content)`);
137
+ return;
138
+ }
139
+ // 追加内容
140
+ fs.appendFileSync(dest, content);
141
+ console.log(`[fullstack-cli] ✓ ${path.basename(dest)} (appended)`);
142
+ }
143
+ /**
144
+ * 设置文件权限
145
+ */
146
+ function setPermissions(permissions, projectRoot) {
147
+ for (const [pattern, mode] of Object.entries(permissions)) {
148
+ // 简单实现:只支持 **/*.sh 这种模式
149
+ if (pattern === '**/*.sh') {
150
+ const scriptsDir = path.join(projectRoot, 'scripts');
151
+ if (fs.existsSync(scriptsDir)) {
152
+ const files = fs.readdirSync(scriptsDir);
153
+ files.forEach(file => {
154
+ if (file.endsWith('.sh')) {
155
+ const filePath = path.join(scriptsDir, file);
156
+ fs.chmodSync(filePath, mode);
157
+ }
158
+ });
159
+ }
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("drizzle-kit").Config;
2
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+ const outputDir = process.env.__DRIZZLE_OUT_DIR__ || './server/database/.introspect';
3
+ const schemaPath = process.env.__DRIZZLE_SCHEMA_PATH__ || './server/database/schema.ts';
4
+ const parsedUrl = new URL(process.env.SUDA_DATABASE_URL || '');
5
+ const schemaFilter = parsedUrl.searchParams.get('schema')?.split(',') ?? [];
6
+ export default defineConfig({
7
+ schema: schemaPath,
8
+ out: outputDir,
9
+ tablesFilter: ['*'],
10
+ schemaFilter,
11
+ dialect: 'postgresql',
12
+ dbCredentials: {
13
+ host: parsedUrl.hostname,
14
+ port: Number(parsedUrl.port) || 5432,
15
+ user: decodeURIComponent(parsedUrl.username),
16
+ password: decodeURIComponent(parsedUrl.password),
17
+ database: parsedUrl.pathname.split('/')[1],
18
+ },
19
+ });
@@ -12,11 +12,10 @@ export interface SyncRule {
12
12
  /** 是否覆盖已存在的文件(仅 directory 和 file 类型有效) */
13
13
  overwrite?: boolean;
14
14
  }
15
- export interface PostinstallConfig {
15
+ export interface SyncConfig {
16
16
  /** 派生规则 */
17
17
  sync: SyncRule[];
18
18
  /** 文件权限设置 */
19
19
  permissions?: Record<string, number>;
20
20
  }
21
- declare const config: PostinstallConfig;
22
- export default config;
21
+ export declare const syncConfig: SyncConfig;
@@ -2,7 +2,7 @@
2
2
  * fullstack-cli 派生配置
3
3
  * 定义哪些文件/目录需要同步到用户项目
4
4
  */
5
- const config = {
5
+ export const syncConfig = {
6
6
  // 派生规则
7
7
  sync: [
8
8
  // 1. 派生 scripts 目录(总是覆盖)
@@ -12,23 +12,22 @@ const config = {
12
12
  type: 'directory',
13
13
  overwrite: true,
14
14
  },
15
- // 2. 追加内容到 .gitignore
16
- {
17
- from: 'templates/.gitignore.append',
18
- to: '.gitignore',
19
- type: 'append',
20
- },
15
+ // // 2. 追加内容到 .gitignore
16
+ // {
17
+ // from: 'templates/.gitignore.append',
18
+ // to: '.gitignore',
19
+ // type: 'append',
20
+ // },
21
21
  // 3. 派生 server/type.ts 文件(总是覆盖)
22
- {
23
- from: 'templates/server/type.ts',
24
- to: 'server/type.ts',
25
- type: 'file',
26
- },
22
+ // {
23
+ // from: 'templates/server/global.d.ts',
24
+ // to: 'server/types/global.d.ts',
25
+ // type: 'file',
26
+ // },
27
27
  ],
28
28
  // 文件权限设置
29
29
  permissions: {
30
- // 所有 .sh 文件设置为可执行
31
- '**/*.sh': 0o755,
30
+ // 所有 .sh 文件设置为可执行
31
+ // '**/*.sh': 0o755,
32
32
  },
33
33
  };
34
- export default config;
package/dist/index.d.ts CHANGED
@@ -1,10 +1 @@
1
- /**
2
- * @lark-apaas/fullstack-cli
3
- *
4
- * CLI tool for fullstack template management
5
- */
6
- export declare const version = "1.0.0";
7
- declare const _default: {
8
- version: string;
9
- };
10
- export default _default;
1
+ export {};
package/dist/index.js CHANGED
@@ -1,9 +1,52 @@
1
- /**
2
- * @lark-apaas/fullstack-cli
3
- *
4
- * CLI tool for fullstack template management
5
- */
6
- export const version = '1.0.0';
7
- export default {
8
- version,
9
- };
1
+ import { fileURLToPath } from 'node:url';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import cac from 'cac';
5
+ import { run as runGenDbSchema } from './commands/gen-db-schema.js';
6
+ import { run as runSync } from './commands/sync.js';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ // 读取 package.json
10
+ const pkgPath = path.join(__dirname, '../package.json');
11
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
12
+ const cli = cac('fullstack-cli');
13
+ // 设置全局描述和版本
14
+ cli
15
+ .version(pkg.version)
16
+ .help();
17
+ // gen-db-schema 命令
18
+ cli
19
+ .command('gen-db-schema', 'Generate database schema from existing database')
20
+ .option('--output <path>', 'Output path for schema', {
21
+ default: 'server/database/schema.ts',
22
+ })
23
+ .option('--schema-filter <schemas>', 'Schema filter, comma-separated')
24
+ .option('--tables-filter <tables>', 'Tables filter, comma-separated')
25
+ .example('fullstack-cli gen-db-schema')
26
+ .example('fullstack-cli gen-db-schema --output custom/schema.ts')
27
+ .action(async (options) => {
28
+ try {
29
+ await runGenDbSchema(options);
30
+ }
31
+ catch (error) {
32
+ console.error(`Failed to execute command:`, error.message);
33
+ console.error(error.stack);
34
+ process.exit(1);
35
+ }
36
+ });
37
+ // sync 命令
38
+ cli
39
+ .command('sync', 'Sync template files (scripts, configs) to user project')
40
+ .example('fullstack-cli sync')
41
+ .action(async () => {
42
+ try {
43
+ await runSync();
44
+ }
45
+ catch (error) {
46
+ console.error(`Failed to execute command:`, error.message);
47
+ console.error(error.stack);
48
+ process.exit(1);
49
+ }
50
+ });
51
+ // 解析命令行参数
52
+ cli.parse();
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "1.0.0",
4
4
  "description": "CLI tool for fullstack template management",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
7
- "bin": "./bin/cli.mjs",
8
+ "bin": "./bin/cli.js",
8
9
  "files": [
9
10
  "dist",
10
11
  "templates",
@@ -12,8 +13,7 @@
12
13
  ],
13
14
  "scripts": {
14
15
  "build": "tsc",
15
- "prepublishOnly": "npm run build",
16
- "postinstall": "node bin/sync-scripts.js"
16
+ "prepublishOnly": "npm run build"
17
17
  },
18
18
  "keywords": [
19
19
  "fullstack",
@@ -30,6 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@lark-apaas/devtool-kits": "^1.0.0",
33
+ "cac": "^6.7.14",
33
34
  "dotenv": "^16.0.0",
34
35
  "drizzle-kit": "0.31.5"
35
36
  },
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env bash
2
+ # This file is auto-generated by @lark-apaas/fullstack-cli
3
+
2
4
  set -euo pipefail
3
5
 
4
6
  ROOT_DIR="$(pwd)"
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env bash
2
+ # This file is auto-generated by @lark-apaas/fullstack-cli
3
+
2
4
  set -euo pipefail
3
5
 
4
6
  # Ensure the script always runs from the project root
@@ -1,5 +1,6 @@
1
+ // This file is auto-generated by @lark-apaas/fullstack-cli
1
2
  import { NestFactory } from '@nestjs/core';
2
- import { DevToolsModule } from '@lark-apaas/fullstack-nestjs-core';
3
+ import { DevToolsV2Module } from '@lark-apaas/fullstack-nestjs-core';
3
4
 
4
5
  import { AppModule } from '../server/app.module';
5
6
 
@@ -17,7 +18,7 @@ async function generateOpenApi() {
17
18
  app.setGlobalPrefix(basePath);
18
19
  }
19
20
 
20
- await DevToolsModule.mount(app, {
21
+ await DevToolsV2Module.mount(app, {
21
22
  basePath,
22
23
  docsPath: '/api_docs',
23
24
  needSetupServer: false,
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ # This file is auto-generated by @lark-apaas/fullstack-cli
2
3
 
3
4
  # 生产环境下,启动服务
4
5
  npm run start
@@ -0,0 +1,19 @@
1
+ // automatically generated by fullstack-nestjs-core, do not edit manually
2
+ declare namespace Express {
3
+ interface Request {
4
+ // don't depend on this field, it's used by client to get platform info
5
+ __platform_data__?: {
6
+ userId?: string;
7
+ tenantId?: string;
8
+ env?: string;
9
+ csrfToken?: string;
10
+ [key: string]: unknown;
11
+ };
12
+ userContext: {
13
+ userId?: string;
14
+ tenantId?: number;
15
+ appId?: string;
16
+ },
17
+ csrfToken?: string;
18
+ }
19
+ }
package/bin/cli.mjs DELETED
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { fileURLToPath } from 'node:url';
4
- import path from 'node:path';
5
- import { createRequire } from 'node:module';
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = path.dirname(__filename);
9
- const require = createRequire(import.meta.url);
10
-
11
- const command = process.argv[2];
12
- const distPath = path.join(__dirname, '..', 'dist');
13
-
14
- async function main() {
15
- try {
16
- switch (command) {
17
- case 'gen-db-schema': {
18
- const modulePath = path.join(distPath, 'commands', 'gen-db-schema.js');
19
- const { run } = await import(modulePath);
20
- await run();
21
- break;
22
- }
23
- case 'sync':
24
- case 'postinstall': {
25
- // 调用原有的 sync-scripts.js (CommonJS)
26
- require('./sync-scripts.js');
27
- break;
28
- }
29
- case '--version':
30
- case '-v': {
31
- const pkg = require('../package.json');
32
- console.log(pkg.version);
33
- break;
34
- }
35
- case '--help':
36
- case '-h':
37
- case undefined: {
38
- showHelp();
39
- break;
40
- }
41
- default: {
42
- console.error(`Unknown command: ${command}`);
43
- console.error('');
44
- showHelp();
45
- process.exit(1);
46
- }
47
- }
48
- } catch (error) {
49
- console.error(`Failed to execute command "${command}":`, error.message);
50
- console.error(error.stack);
51
- process.exit(1);
52
- }
53
- }
54
-
55
- function showHelp() {
56
- console.log(`
57
- Usage: fullstack-cli <command> [options]
58
-
59
- Commands:
60
- gen-db-schema Generate database schema from existing database
61
- sync Sync template files to user project (internal use)
62
-
63
- Options:
64
- --version, -v Show version number
65
- --help, -h Show help
66
-
67
- Environment Variables:
68
- # gen-db-schema
69
- SUDA_DATABASE_URL Database connection URL (required)
70
- DB_SCHEMA_OUTPUT Output path for schema (default: server/database/schema.ts)
71
- DRIZZLE_SCHEMA_FILTER Schema filter, comma-separated (optional)
72
- DRIZZLE_TABLES_FILTER Tables filter (default: *)
73
-
74
- Examples:
75
- $ fullstack-cli gen-db-schema
76
- $ DB_SCHEMA_OUTPUT=custom/schema.ts fullstack-cli gen-db-schema
77
-
78
- Scripts (auto-generated):
79
- scripts/gen-openapi.ts - Generate OpenAPI documentation (use: npm run gen:openapi)
80
- scripts/gen-db-schema.ts - Generate database schema (deprecated, use CLI command)
81
- `);
82
- }
83
-
84
- main();
@@ -1,183 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const path = require('path');
4
- const fs = require('fs');
5
-
6
- // 主函数
7
- async function main() {
8
- // 检测是否在用户项目中(通过 INIT_CWD 判断)
9
- const userProjectRoot = process.env.INIT_CWD || process.cwd();
10
- const pluginRoot = path.resolve(__dirname, '..');
11
-
12
- // 只有在插件自己的开发目录中才跳过(避免在开发插件时触发 sync)
13
- if (userProjectRoot === pluginRoot) {
14
- console.log('[fullstack-cli] Skip syncing (installing plugin itself)');
15
- process.exit(0);
16
- }
17
-
18
- // 检查是否存在 package.json,确保是有效的项目
19
- const userPackageJson = path.join(userProjectRoot, 'package.json');
20
- if (!fs.existsSync(userPackageJson)) {
21
- console.log('[fullstack-cli] Skip syncing (not a valid npm project)');
22
- process.exit(0);
23
- }
24
-
25
- try {
26
- console.log('[fullstack-cli] Starting sync...');
27
-
28
- // 加载配置文件(从 dist 目录加载编译后的配置)
29
- const { default: config } = await import('../dist/postinstall.config.js');
30
-
31
- if (!config || !config.sync) {
32
- console.warn('[fullstack-cli] No sync configuration found');
33
- process.exit(0);
34
- }
35
-
36
- // 执行同步规则
37
- for (const rule of config.sync) {
38
- await syncRule(rule, pluginRoot, userProjectRoot);
39
- }
40
-
41
- // 设置权限
42
- if (config.permissions) {
43
- setPermissions(config.permissions, userProjectRoot);
44
- }
45
-
46
- console.log('[fullstack-cli] Sync completed successfully ✅');
47
- } catch (error) {
48
- console.error('[fullstack-cli] Failed to sync:', error.message);
49
- // 不阻塞安装过程
50
- process.exit(0);
51
- }
52
- }
53
-
54
- /**
55
- * 执行单个同步规则
56
- */
57
- async function syncRule(rule, pluginRoot, userProjectRoot) {
58
- const srcPath = path.join(pluginRoot, rule.from);
59
- const destPath = path.join(userProjectRoot, rule.to);
60
-
61
- if (!fs.existsSync(srcPath)) {
62
- console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
63
- return;
64
- }
65
-
66
- switch (rule.type) {
67
- case 'directory':
68
- syncDirectory(srcPath, destPath, rule.overwrite);
69
- break;
70
- case 'file':
71
- syncFile(srcPath, destPath, rule.overwrite);
72
- break;
73
- case 'append':
74
- appendToFile(srcPath, destPath);
75
- break;
76
- default:
77
- console.warn(`[fullstack-cli] Unknown sync type: ${rule.type}`);
78
- }
79
- }
80
-
81
- /**
82
- * 同步单个文件
83
- */
84
- function syncFile(src, dest, overwrite = true) {
85
- // 确保目标目录存在
86
- const destDir = path.dirname(dest);
87
- if (!fs.existsSync(destDir)) {
88
- fs.mkdirSync(destDir, { recursive: true });
89
- }
90
-
91
- // 检查目标文件是否存在
92
- if (fs.existsSync(dest) && !overwrite) {
93
- console.log(`[fullstack-cli] ○ ${path.basename(dest)} (skipped, already exists)`);
94
- return;
95
- }
96
-
97
- // 复制文件
98
- fs.copyFileSync(src, dest);
99
- console.log(`[fullstack-cli] ✓ ${path.basename(dest)}`);
100
- }
101
-
102
- /**
103
- * 同步整个目录
104
- */
105
- function syncDirectory(src, dest, overwrite = true) {
106
- // 确保目标目录存在
107
- if (!fs.existsSync(dest)) {
108
- fs.mkdirSync(dest, { recursive: true });
109
- }
110
-
111
- // 读取源目录所有文件
112
- const files = fs.readdirSync(src);
113
- let count = 0;
114
-
115
- files.forEach(file => {
116
- const srcFile = path.join(src, file);
117
- const destFile = path.join(dest, file);
118
- const stats = fs.statSync(srcFile);
119
-
120
- if (stats.isDirectory()) {
121
- // 递归处理子目录
122
- syncDirectory(srcFile, destFile, overwrite);
123
- } else {
124
- // 复制文件
125
- if (overwrite || !fs.existsSync(destFile)) {
126
- fs.copyFileSync(srcFile, destFile);
127
- console.log(`[fullstack-cli] ✓ ${path.relative(dest, destFile)}`);
128
- count++;
129
- }
130
- }
131
- });
132
-
133
- if (count > 0) {
134
- console.log(`[fullstack-cli] Synced ${count} files to ${path.basename(dest)}/`);
135
- }
136
- }
137
-
138
- /**
139
- * 追加内容到文件
140
- */
141
- function appendToFile(src, dest) {
142
- const content = fs.readFileSync(src, 'utf-8');
143
-
144
- // 读取目标文件内容(如果存在)
145
- let existingContent = '';
146
- if (fs.existsSync(dest)) {
147
- existingContent = fs.readFileSync(dest, 'utf-8');
148
- }
149
-
150
- // 检查是否已包含相同内容(避免重复追加)
151
- if (existingContent.includes(content.trim())) {
152
- console.log(`[fullstack-cli] ○ ${path.basename(dest)} (already contains content)`);
153
- return;
154
- }
155
-
156
- // 追加内容
157
- fs.appendFileSync(dest, content);
158
- console.log(`[fullstack-cli] ✓ ${path.basename(dest)} (appended)`);
159
- }
160
-
161
- /**
162
- * 设置文件权限
163
- */
164
- function setPermissions(permissions, projectRoot) {
165
- for (const [pattern, mode] of Object.entries(permissions)) {
166
- // 简单实现:只支持 **/*.sh 这种模式
167
- if (pattern === '**/*.sh') {
168
- const scriptsDir = path.join(projectRoot, 'scripts');
169
- if (fs.existsSync(scriptsDir)) {
170
- const files = fs.readdirSync(scriptsDir);
171
- files.forEach(file => {
172
- if (file.endsWith('.sh')) {
173
- const filePath = path.join(scriptsDir, file);
174
- fs.chmodSync(filePath, mode);
175
- }
176
- });
177
- }
178
- }
179
- }
180
- }
181
-
182
- // 运行主函数
183
- main();
@@ -1,25 +0,0 @@
1
- import { defineConfig } from 'drizzle-kit';
2
-
3
- // 这个配置会被 fullstack-cli 在运行时动态处理
4
- // 通过环境变量传递具体参数
5
- const outputDir = process.env.__DRIZZLE_OUT_DIR__ || './server/database/.introspect';
6
- const schemaPath = process.env.__DRIZZLE_SCHEMA_PATH__ || './server/database/schema.ts';
7
-
8
- const parsedUrl = new URL(process.env.SUDA_DATABASE_URL || '');
9
- const schemaFilter = parsedUrl.searchParams.get('schema')?.split(',') ?? [];
10
-
11
- const config = {
12
- schema: schemaPath,
13
- out: outputDir,
14
- tablesFilter: ['*'],
15
- schemaFilter,
16
- dialect: 'postgresql',
17
- dbCredentials: {
18
- host: parsedUrl.hostname,
19
- port: Number(parsedUrl.port) || 5432,
20
- user: decodeURIComponent(parsedUrl.username),
21
- password: decodeURIComponent(parsedUrl.password),
22
- database: parsedUrl.pathname.split('/')[1],
23
- },
24
- }
25
- export default defineConfig(config);
@@ -1,4 +0,0 @@
1
- export interface ServerType {
2
- // 服务器类型
3
- serverType: string;
4
- }