@windrun-huaiin/dev-scripts 10.0.0 → 10.2.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/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  - ✅ **翻译检查**: 检查翻译文件的完整性和一致性
8
8
  - 🧹 **翻译清理**: 自动清理未使用的翻译键
9
9
  - 📝 **博客索引生成**: 自动生成博客索引和月度统计
10
+ - 🧭 **Backend Core 集成**: 生成 Next.js 路由壳、合并 Prisma 模型、同步SQL
10
11
  - ⚙️ **配置驱动**: 支持多种配置方式,适配不同项目结构
11
12
  - 🔧 **CLI工具**: 统一的命令行接口,易于集成到构建流程
12
13
 
@@ -233,6 +234,21 @@ Options:
233
234
  ✅ 共清理 3 个目录或文件。
234
235
  ```
235
236
 
237
+ ### backend-core
238
+
239
+ 为 `@windrun-huaiin/backend-core` 提供路由壳生成与 Prisma 模型合并(需先安装 backend-core,可在 workspace/项目内被 resolve)。
240
+
241
+ ```bash
242
+ dev-scripts backend-core routes:list
243
+ dev-scripts backend-core routes:sync --app-dir src/app --force
244
+ dev-scripts backend-core prisma:sync --schema prisma/schema.prisma
245
+ dev-scripts backend-core migrations:sync --dest prisma --force
246
+ ```
247
+
248
+ - `routes:sync`:在 `app/api` 下生成代理文件,默认 app 目录为 `src/app`,默认跳过已存在文件,`--force` 强制覆盖。
249
+ - `prisma:sync`:把包内模型追加到宿主 schema(默认 `prisma/schema.prisma`),并用宿主 datasource 的 schema 名替换 `@@schema("nextai")`。
250
+ - `migrations:sync`:将包内 `migrations/*.sql` 复制到指定目录(默认 `prisma/`,默认跳过已存在,`--force` 可覆盖)。
251
+
236
252
  ## 支持的翻译模式
237
253
 
238
254
  脚本支持多种翻译使用模式:
@@ -388,4 +404,4 @@ MIT License
388
404
  - [Newspaper Template](https://newspaper-template.org/en)
389
405
  - [breathing exercise](https://breathingexercise.net/en)
390
406
  - [ai directory list](https://aidirectorylist.com/en)
391
- - [reve image directory](https://reveimage.directory/en)
407
+ - [reve image directory](https://reveimage.directory/en)
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ var deepClean = require('./commands/deep-clean.js');
10
10
  var easyChangeset = require('./commands/easy-changeset.js');
11
11
  var generateNextjsArchitecture = require('./commands/generate-nextjs-architecture.js');
12
12
  var createDiaomaoApp = require('./commands/create-diaomao-app.js');
13
+ var backendCore = require('./commands/backend-core.js');
13
14
 
14
15
  // get current working directory, ensure it works in Node.js environment
15
16
  const cwd = typeof process !== 'undefined' ? process.cwd() : '.';
@@ -168,6 +169,7 @@ commander.program
168
169
  }
169
170
  }
170
171
  });
172
+ backendCore.registerBackendCoreCommands(commander.program);
171
173
  // parse command line arguments
172
174
  if (typeof process !== 'undefined') {
173
175
  commander.program.parse(process.argv);
package/dist/cli.mjs CHANGED
@@ -8,6 +8,7 @@ import { deepClean } from './commands/deep-clean.mjs';
8
8
  import { easyChangeset } from './commands/easy-changeset.mjs';
9
9
  import { generateNextjsArchitecture } from './commands/generate-nextjs-architecture.mjs';
10
10
  import { createDiaomaoApp } from './commands/create-diaomao-app.mjs';
11
+ import { registerBackendCoreCommands } from './commands/backend-core.mjs';
11
12
 
12
13
  // get current working directory, ensure it works in Node.js environment
13
14
  const cwd = typeof process !== 'undefined' ? process.cwd() : '.';
@@ -166,6 +167,7 @@ program
166
167
  }
167
168
  }
168
169
  });
170
+ registerBackendCoreCommands(program);
169
171
  // parse command line arguments
170
172
  if (typeof process !== 'undefined') {
171
173
  program.parse(process.argv);
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ type RouteSyncResult = {
3
+ file: string;
4
+ status: string;
5
+ };
6
+ export declare function registerBackendCoreCommands(program: Command): void;
7
+ export declare function syncBackendCoreRoutes(appDirInput: string, force?: boolean, cwd?: string): Promise<RouteSyncResult[]>;
8
+ export declare function listBackendCoreRoutes(): void;
9
+ export declare function syncBackendCorePrisma(schemaPathInput: string, cwd?: string): Promise<string>;
10
+ export declare function syncBackendCoreMigrations(destDirInput: string, force?: boolean, cwd?: string): Promise<RouteSyncResult[]>;
11
+ export {};
12
+ //# sourceMappingURL=backend-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend-core.d.ts","sourceRoot":"","sources":["../../src/commands/backend-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAYnC,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAiCD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,QAiF3D;AAED,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,OAAe,EACtB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CAuB5B;AAED,wBAAgB,qBAAqB,SAKpC;AAED,wBAAsB,qBAAqB,CACzC,eAAe,EAAE,MAAM,EACvB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,MAAM,EACpB,KAAK,GAAE,OAAe,EACtB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,CAgC5B"}
@@ -0,0 +1,259 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var module$1 = require('module');
5
+ var path = require('path');
6
+
7
+ const PACKAGE_NAME = '@windrun-huaiin/backend-core';
8
+ const ROUTES = [
9
+ {
10
+ path: 'webhook/stripe',
11
+ importPath: `${PACKAGE_NAME}/app/api/webhook/stripe/route`,
12
+ methods: ['POST'],
13
+ runtime: 'nodejs'
14
+ },
15
+ {
16
+ path: 'webhook/clerk/user',
17
+ importPath: `${PACKAGE_NAME}/app/api/webhook/clerk/user/route`,
18
+ methods: ['POST']
19
+ },
20
+ {
21
+ path: 'user/anonymous/init',
22
+ importPath: `${PACKAGE_NAME}/app/api/user/anonymous/init/route`,
23
+ methods: ['POST']
24
+ },
25
+ {
26
+ path: 'stripe/checkout',
27
+ importPath: `${PACKAGE_NAME}/app/api/stripe/checkout/route`,
28
+ methods: ['POST']
29
+ },
30
+ {
31
+ path: 'stripe/customer-portal',
32
+ importPath: `${PACKAGE_NAME}/app/api/stripe/customer-portal/route`,
33
+ methods: ['POST']
34
+ }
35
+ ];
36
+ function registerBackendCoreCommands(program) {
37
+ const backendCore = program
38
+ .command('backend-core')
39
+ .usage('backend-core <subcommand> [options]')
40
+ .description('Integration @windrun-huaiin/backend-core, use "dev-scripts backend-core -h" for more msg')
41
+ .addHelpText('after', [
42
+ '',
43
+ 'Examples:',
44
+ ' dev-scripts backend-core routes:list',
45
+ ' dev-scripts backend-core routes:sync --app-dir src/app --force',
46
+ ' dev-scripts backend-core prisma:sync --schema prisma/schema.prisma',
47
+ ' dev-scripts backend-core migrations:sync --dest prisma --force',
48
+ '',
49
+ 'Defaults:',
50
+ ' routes:sync --app-dir src/app (generates under <app-dir>/api)',
51
+ ' prisma:sync --schema prisma/schema.prisma',
52
+ ' migrations:sync --dest prisma (copies migrations/*.sql into dest)',
53
+ ].join('\n'));
54
+ backendCore
55
+ .command('routes:sync')
56
+ .description('Generate Next.js route proxy files that re-export backend-core handlers')
57
+ .option('--app-dir <dir>', 'App directory containing api folder', 'src/app')
58
+ .option('--force', 'Overwrite existing files', false)
59
+ .action(async (opts) => {
60
+ const cwd = process.cwd();
61
+ try {
62
+ const results = await syncBackendCoreRoutes(opts.appDir, opts.force, cwd);
63
+ console.log('Routes sync results:');
64
+ for (const r of results) {
65
+ console.log(`- ${r.status} :: ${r.file}`);
66
+ }
67
+ }
68
+ catch (error) {
69
+ console.error('routes:sync failed:', error.message);
70
+ process.exitCode = 1;
71
+ }
72
+ });
73
+ backendCore
74
+ .command('routes:list')
75
+ .description('List available backend-core routes')
76
+ .action(() => {
77
+ listBackendCoreRoutes();
78
+ });
79
+ backendCore
80
+ .command('prisma:sync')
81
+ .description('Append backend-core models into host prisma/schema.prisma (datasource/generator stay from host)')
82
+ .option('--schema <file>', 'Host schema.prisma path', 'prisma/schema.prisma')
83
+ .action(async (opts) => {
84
+ const cwd = process.cwd();
85
+ try {
86
+ const message = await syncBackendCorePrisma(opts.schema, cwd);
87
+ console.log(message);
88
+ }
89
+ catch (error) {
90
+ console.error('prisma:sync failed:', error.message);
91
+ process.exitCode = 1;
92
+ }
93
+ });
94
+ backendCore
95
+ .command('migrations:sync')
96
+ .description('Copy backend-core SQL migrations into the host directory')
97
+ .option('--dest <dir>', 'Target migrations directory', 'prisma')
98
+ .option('--force', 'Overwrite existing files', false)
99
+ .action(async (opts) => {
100
+ const cwd = process.cwd();
101
+ try {
102
+ const results = await syncBackendCoreMigrations(opts.dest, opts.force, cwd);
103
+ console.log('Migrations sync results:');
104
+ for (const r of results) {
105
+ console.log(`- ${r.status} :: ${r.file}`);
106
+ }
107
+ }
108
+ catch (error) {
109
+ console.error('migrations:sync failed:', error.message);
110
+ process.exitCode = 1;
111
+ }
112
+ });
113
+ }
114
+ async function syncBackendCoreRoutes(appDirInput, force = false, cwd = process.cwd()) {
115
+ const appDir = path.resolve(cwd, appDirInput);
116
+ const results = [];
117
+ for (const route of ROUTES) {
118
+ const targetDir = path.join(appDir, 'api', route.path);
119
+ const targetFile = path.join(targetDir, 'route.ts');
120
+ try {
121
+ await fs.promises.mkdir(targetDir, { recursive: true });
122
+ const exists = await fileExists(targetFile);
123
+ if (exists && !force) {
124
+ results.push({ file: targetFile, status: 'skip (exists)' });
125
+ continue;
126
+ }
127
+ const content = buildProxy(route);
128
+ await fs.promises.writeFile(targetFile, content, 'utf8');
129
+ results.push({ file: targetFile, status: exists ? 'overwritten' : 'created' });
130
+ }
131
+ catch (error) {
132
+ results.push({ file: targetFile, status: `error: ${error.message}` });
133
+ }
134
+ }
135
+ return results;
136
+ }
137
+ function listBackendCoreRoutes() {
138
+ console.log('Available backend-core routes (relative to app/api):');
139
+ ROUTES.forEach((route) => {
140
+ console.log(`- ${route.path} [${route.methods.join(', ')}]`);
141
+ });
142
+ }
143
+ async function syncBackendCorePrisma(schemaPathInput, cwd = process.cwd()) {
144
+ const hostSchemaPath = path.resolve(cwd, schemaPathInput);
145
+ const pkgSchemaPath = path.resolve(getBackendPackageRoot(cwd), 'prisma', 'schema.prisma');
146
+ // Validate required files exist inside the installed backend-core package so third parties get a clear hint.
147
+ await assertExists(pkgSchemaPath, `backend-core prisma/schema.prisma not found at ${pkgSchemaPath}. Ensure @windrun-huaiin/backend-core is installed and publishes prisma/ to npm.`);
148
+ const [hostRaw, pkgRaw] = await Promise.all([
149
+ fs.promises.readFile(hostSchemaPath, 'utf8'),
150
+ fs.promises.readFile(pkgSchemaPath, 'utf8')
151
+ ]);
152
+ const hostSchemaName = extractSchemaName(hostRaw);
153
+ const pkgModels = extractModels(pkgRaw, hostSchemaName);
154
+ if (!pkgModels.trim()) {
155
+ return 'No models extracted from package schema, nothing to append.';
156
+ }
157
+ if (hostRaw.includes('=== backend-core models ===')) {
158
+ return 'Marker already found in host schema, skipping append to avoid duplicates.';
159
+ }
160
+ const appended = [
161
+ hostRaw.trimEnd(),
162
+ '',
163
+ '// === backend-core models ===',
164
+ pkgModels.trim(),
165
+ ''
166
+ ].join('\n');
167
+ await fs.promises.writeFile(hostSchemaPath, appended, 'utf8');
168
+ const schemaNote = hostSchemaName && hostSchemaName !== 'nextai'
169
+ ? `Replaced @@schema("nextai") with @@schema("${hostSchemaName}")`
170
+ : '';
171
+ return ['Appended backend-core models to', hostSchemaPath, schemaNote].filter(Boolean).join(' ');
172
+ }
173
+ async function syncBackendCoreMigrations(destDirInput, force = false, cwd = process.cwd()) {
174
+ const pkgRoot = getBackendPackageRoot(cwd);
175
+ const sourceDir = path.join(pkgRoot, 'migrations');
176
+ const destDir = path.resolve(cwd, destDirInput);
177
+ await assertExists(sourceDir, `backend-core migrations folder not found at ${sourceDir}. Ensure the package publishes migrations/.`);
178
+ await fs.promises.mkdir(destDir, { recursive: true });
179
+ const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });
180
+ const sqlFiles = entries.filter((e) => e.isFile() && e.name.endsWith('.sql'));
181
+ const results = [];
182
+ for (const file of sqlFiles) {
183
+ const from = path.join(sourceDir, file.name);
184
+ const to = path.join(destDir, file.name);
185
+ const exists = await fileExists(to);
186
+ if (exists && !force) {
187
+ results.push({ file: to, status: 'skip (exists)' });
188
+ continue;
189
+ }
190
+ await fs.promises.copyFile(from, to);
191
+ results.push({ file: to, status: exists ? 'overwritten' : 'copied' });
192
+ }
193
+ if (sqlFiles.length === 0) {
194
+ results.push({ file: destDir, status: 'no migrations found in package' });
195
+ }
196
+ return results;
197
+ }
198
+ function buildProxy(route) {
199
+ const exports = [
200
+ ...route.methods.map((method) => `export { ${method} } from '${route.importPath}';`)
201
+ ];
202
+ const runtimeLine = route.runtime ? `export const runtime = '${route.runtime}';` : null;
203
+ return [
204
+ '// Auto-generated by dev-scripts backend-core routes:sync',
205
+ '// Do not edit manually unless you want to override the default handler.',
206
+ ...exports,
207
+ ...(runtimeLine ? [runtimeLine] : []),
208
+ ''
209
+ ].join('\n');
210
+ }
211
+ async function fileExists(file) {
212
+ try {
213
+ await fs.promises.access(file);
214
+ return true;
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ }
220
+ async function assertExists(file, message) {
221
+ const exists = await fileExists(file);
222
+ if (!exists) {
223
+ throw new Error(message);
224
+ }
225
+ }
226
+ function getBackendPackageRoot(cwd) {
227
+ const resolver = module$1.createRequire(path.join(cwd, 'package.json'));
228
+ try {
229
+ const pkgJsonPath = resolver.resolve(`${PACKAGE_NAME}/package.json`);
230
+ return path.dirname(pkgJsonPath);
231
+ }
232
+ catch (error) {
233
+ const message = `Cannot resolve ${PACKAGE_NAME} from ${cwd}. ` +
234
+ 'Install the package or check your workspace configuration.';
235
+ throw new Error(message);
236
+ }
237
+ }
238
+ function extractSchemaName(schema) {
239
+ const schemaMatch = schema.match(/schema\s*=\s*"([^"]+)"/);
240
+ if (schemaMatch)
241
+ return schemaMatch[1];
242
+ const schemasMatch = schema.match(/schemas\s*=\s*\[\s*"([^"]+)"/);
243
+ if (schemasMatch)
244
+ return schemasMatch[1];
245
+ return 'nextai';
246
+ }
247
+ function extractModels(schema, targetSchemaName) {
248
+ const idx = schema.indexOf('model ');
249
+ if (idx === -1)
250
+ return '';
251
+ const models = schema.slice(idx);
252
+ return models.replace(/@@schema\("nextai"\)/g, `@@schema("${targetSchemaName}")`);
253
+ }
254
+
255
+ exports.listBackendCoreRoutes = listBackendCoreRoutes;
256
+ exports.registerBackendCoreCommands = registerBackendCoreCommands;
257
+ exports.syncBackendCoreMigrations = syncBackendCoreMigrations;
258
+ exports.syncBackendCorePrisma = syncBackendCorePrisma;
259
+ exports.syncBackendCoreRoutes = syncBackendCoreRoutes;
@@ -0,0 +1,253 @@
1
+ import { promises } from 'fs';
2
+ import { createRequire } from 'module';
3
+ import path from 'path';
4
+
5
+ const PACKAGE_NAME = '@windrun-huaiin/backend-core';
6
+ const ROUTES = [
7
+ {
8
+ path: 'webhook/stripe',
9
+ importPath: `${PACKAGE_NAME}/app/api/webhook/stripe/route`,
10
+ methods: ['POST'],
11
+ runtime: 'nodejs'
12
+ },
13
+ {
14
+ path: 'webhook/clerk/user',
15
+ importPath: `${PACKAGE_NAME}/app/api/webhook/clerk/user/route`,
16
+ methods: ['POST']
17
+ },
18
+ {
19
+ path: 'user/anonymous/init',
20
+ importPath: `${PACKAGE_NAME}/app/api/user/anonymous/init/route`,
21
+ methods: ['POST']
22
+ },
23
+ {
24
+ path: 'stripe/checkout',
25
+ importPath: `${PACKAGE_NAME}/app/api/stripe/checkout/route`,
26
+ methods: ['POST']
27
+ },
28
+ {
29
+ path: 'stripe/customer-portal',
30
+ importPath: `${PACKAGE_NAME}/app/api/stripe/customer-portal/route`,
31
+ methods: ['POST']
32
+ }
33
+ ];
34
+ function registerBackendCoreCommands(program) {
35
+ const backendCore = program
36
+ .command('backend-core')
37
+ .usage('backend-core <subcommand> [options]')
38
+ .description('Integration @windrun-huaiin/backend-core, use "dev-scripts backend-core -h" for more msg')
39
+ .addHelpText('after', [
40
+ '',
41
+ 'Examples:',
42
+ ' dev-scripts backend-core routes:list',
43
+ ' dev-scripts backend-core routes:sync --app-dir src/app --force',
44
+ ' dev-scripts backend-core prisma:sync --schema prisma/schema.prisma',
45
+ ' dev-scripts backend-core migrations:sync --dest prisma --force',
46
+ '',
47
+ 'Defaults:',
48
+ ' routes:sync --app-dir src/app (generates under <app-dir>/api)',
49
+ ' prisma:sync --schema prisma/schema.prisma',
50
+ ' migrations:sync --dest prisma (copies migrations/*.sql into dest)',
51
+ ].join('\n'));
52
+ backendCore
53
+ .command('routes:sync')
54
+ .description('Generate Next.js route proxy files that re-export backend-core handlers')
55
+ .option('--app-dir <dir>', 'App directory containing api folder', 'src/app')
56
+ .option('--force', 'Overwrite existing files', false)
57
+ .action(async (opts) => {
58
+ const cwd = process.cwd();
59
+ try {
60
+ const results = await syncBackendCoreRoutes(opts.appDir, opts.force, cwd);
61
+ console.log('Routes sync results:');
62
+ for (const r of results) {
63
+ console.log(`- ${r.status} :: ${r.file}`);
64
+ }
65
+ }
66
+ catch (error) {
67
+ console.error('routes:sync failed:', error.message);
68
+ process.exitCode = 1;
69
+ }
70
+ });
71
+ backendCore
72
+ .command('routes:list')
73
+ .description('List available backend-core routes')
74
+ .action(() => {
75
+ listBackendCoreRoutes();
76
+ });
77
+ backendCore
78
+ .command('prisma:sync')
79
+ .description('Append backend-core models into host prisma/schema.prisma (datasource/generator stay from host)')
80
+ .option('--schema <file>', 'Host schema.prisma path', 'prisma/schema.prisma')
81
+ .action(async (opts) => {
82
+ const cwd = process.cwd();
83
+ try {
84
+ const message = await syncBackendCorePrisma(opts.schema, cwd);
85
+ console.log(message);
86
+ }
87
+ catch (error) {
88
+ console.error('prisma:sync failed:', error.message);
89
+ process.exitCode = 1;
90
+ }
91
+ });
92
+ backendCore
93
+ .command('migrations:sync')
94
+ .description('Copy backend-core SQL migrations into the host directory')
95
+ .option('--dest <dir>', 'Target migrations directory', 'prisma')
96
+ .option('--force', 'Overwrite existing files', false)
97
+ .action(async (opts) => {
98
+ const cwd = process.cwd();
99
+ try {
100
+ const results = await syncBackendCoreMigrations(opts.dest, opts.force, cwd);
101
+ console.log('Migrations sync results:');
102
+ for (const r of results) {
103
+ console.log(`- ${r.status} :: ${r.file}`);
104
+ }
105
+ }
106
+ catch (error) {
107
+ console.error('migrations:sync failed:', error.message);
108
+ process.exitCode = 1;
109
+ }
110
+ });
111
+ }
112
+ async function syncBackendCoreRoutes(appDirInput, force = false, cwd = process.cwd()) {
113
+ const appDir = path.resolve(cwd, appDirInput);
114
+ const results = [];
115
+ for (const route of ROUTES) {
116
+ const targetDir = path.join(appDir, 'api', route.path);
117
+ const targetFile = path.join(targetDir, 'route.ts');
118
+ try {
119
+ await promises.mkdir(targetDir, { recursive: true });
120
+ const exists = await fileExists(targetFile);
121
+ if (exists && !force) {
122
+ results.push({ file: targetFile, status: 'skip (exists)' });
123
+ continue;
124
+ }
125
+ const content = buildProxy(route);
126
+ await promises.writeFile(targetFile, content, 'utf8');
127
+ results.push({ file: targetFile, status: exists ? 'overwritten' : 'created' });
128
+ }
129
+ catch (error) {
130
+ results.push({ file: targetFile, status: `error: ${error.message}` });
131
+ }
132
+ }
133
+ return results;
134
+ }
135
+ function listBackendCoreRoutes() {
136
+ console.log('Available backend-core routes (relative to app/api):');
137
+ ROUTES.forEach((route) => {
138
+ console.log(`- ${route.path} [${route.methods.join(', ')}]`);
139
+ });
140
+ }
141
+ async function syncBackendCorePrisma(schemaPathInput, cwd = process.cwd()) {
142
+ const hostSchemaPath = path.resolve(cwd, schemaPathInput);
143
+ const pkgSchemaPath = path.resolve(getBackendPackageRoot(cwd), 'prisma', 'schema.prisma');
144
+ // Validate required files exist inside the installed backend-core package so third parties get a clear hint.
145
+ await assertExists(pkgSchemaPath, `backend-core prisma/schema.prisma not found at ${pkgSchemaPath}. Ensure @windrun-huaiin/backend-core is installed and publishes prisma/ to npm.`);
146
+ const [hostRaw, pkgRaw] = await Promise.all([
147
+ promises.readFile(hostSchemaPath, 'utf8'),
148
+ promises.readFile(pkgSchemaPath, 'utf8')
149
+ ]);
150
+ const hostSchemaName = extractSchemaName(hostRaw);
151
+ const pkgModels = extractModels(pkgRaw, hostSchemaName);
152
+ if (!pkgModels.trim()) {
153
+ return 'No models extracted from package schema, nothing to append.';
154
+ }
155
+ if (hostRaw.includes('=== backend-core models ===')) {
156
+ return 'Marker already found in host schema, skipping append to avoid duplicates.';
157
+ }
158
+ const appended = [
159
+ hostRaw.trimEnd(),
160
+ '',
161
+ '// === backend-core models ===',
162
+ pkgModels.trim(),
163
+ ''
164
+ ].join('\n');
165
+ await promises.writeFile(hostSchemaPath, appended, 'utf8');
166
+ const schemaNote = hostSchemaName && hostSchemaName !== 'nextai'
167
+ ? `Replaced @@schema("nextai") with @@schema("${hostSchemaName}")`
168
+ : '';
169
+ return ['Appended backend-core models to', hostSchemaPath, schemaNote].filter(Boolean).join(' ');
170
+ }
171
+ async function syncBackendCoreMigrations(destDirInput, force = false, cwd = process.cwd()) {
172
+ const pkgRoot = getBackendPackageRoot(cwd);
173
+ const sourceDir = path.join(pkgRoot, 'migrations');
174
+ const destDir = path.resolve(cwd, destDirInput);
175
+ await assertExists(sourceDir, `backend-core migrations folder not found at ${sourceDir}. Ensure the package publishes migrations/.`);
176
+ await promises.mkdir(destDir, { recursive: true });
177
+ const entries = await promises.readdir(sourceDir, { withFileTypes: true });
178
+ const sqlFiles = entries.filter((e) => e.isFile() && e.name.endsWith('.sql'));
179
+ const results = [];
180
+ for (const file of sqlFiles) {
181
+ const from = path.join(sourceDir, file.name);
182
+ const to = path.join(destDir, file.name);
183
+ const exists = await fileExists(to);
184
+ if (exists && !force) {
185
+ results.push({ file: to, status: 'skip (exists)' });
186
+ continue;
187
+ }
188
+ await promises.copyFile(from, to);
189
+ results.push({ file: to, status: exists ? 'overwritten' : 'copied' });
190
+ }
191
+ if (sqlFiles.length === 0) {
192
+ results.push({ file: destDir, status: 'no migrations found in package' });
193
+ }
194
+ return results;
195
+ }
196
+ function buildProxy(route) {
197
+ const exports = [
198
+ ...route.methods.map((method) => `export { ${method} } from '${route.importPath}';`)
199
+ ];
200
+ const runtimeLine = route.runtime ? `export const runtime = '${route.runtime}';` : null;
201
+ return [
202
+ '// Auto-generated by dev-scripts backend-core routes:sync',
203
+ '// Do not edit manually unless you want to override the default handler.',
204
+ ...exports,
205
+ ...(runtimeLine ? [runtimeLine] : []),
206
+ ''
207
+ ].join('\n');
208
+ }
209
+ async function fileExists(file) {
210
+ try {
211
+ await promises.access(file);
212
+ return true;
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ }
218
+ async function assertExists(file, message) {
219
+ const exists = await fileExists(file);
220
+ if (!exists) {
221
+ throw new Error(message);
222
+ }
223
+ }
224
+ function getBackendPackageRoot(cwd) {
225
+ const resolver = createRequire(path.join(cwd, 'package.json'));
226
+ try {
227
+ const pkgJsonPath = resolver.resolve(`${PACKAGE_NAME}/package.json`);
228
+ return path.dirname(pkgJsonPath);
229
+ }
230
+ catch (error) {
231
+ const message = `Cannot resolve ${PACKAGE_NAME} from ${cwd}. ` +
232
+ 'Install the package or check your workspace configuration.';
233
+ throw new Error(message);
234
+ }
235
+ }
236
+ function extractSchemaName(schema) {
237
+ const schemaMatch = schema.match(/schema\s*=\s*"([^"]+)"/);
238
+ if (schemaMatch)
239
+ return schemaMatch[1];
240
+ const schemasMatch = schema.match(/schemas\s*=\s*\[\s*"([^"]+)"/);
241
+ if (schemasMatch)
242
+ return schemasMatch[1];
243
+ return 'nextai';
244
+ }
245
+ function extractModels(schema, targetSchemaName) {
246
+ const idx = schema.indexOf('model ');
247
+ if (idx === -1)
248
+ return '';
249
+ const models = schema.slice(idx);
250
+ return models.replace(/@@schema\("nextai"\)/g, `@@schema("${targetSchemaName}")`);
251
+ }
252
+
253
+ export { listBackendCoreRoutes, registerBackendCoreCommands, syncBackendCoreMigrations, syncBackendCorePrisma, syncBackendCoreRoutes };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { checkTranslations } from '@dev-scripts/commands/check-translations';
2
2
  export { cleanTranslations } from '@dev-scripts/commands/clean-translations';
3
3
  export { generateBlogIndex } from '@dev-scripts/commands/generate-blog-index';
4
+ export { registerBackendCoreCommands, syncBackendCoreRoutes, listBackendCoreRoutes, syncBackendCorePrisma, syncBackendCoreMigrations } from '@dev-scripts/commands/backend-core';
4
5
  export { loadConfig, validateConfig } from '@dev-scripts/config';
5
6
  export type { DevScriptsConfig, PackageJsonDevScripts } from '@dev-scripts/config/schema';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAA;AAG7E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAChE,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAA;AAC7E,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,yBAAyB,EAC1B,MAAM,oCAAoC,CAAA;AAG3C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAChE,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var checkTranslations = require('./commands/check-translations.js');
4
4
  var cleanTranslations = require('./commands/clean-translations.js');
5
5
  var generateBlogIndex = require('./commands/generate-blog-index.js');
6
+ var backendCore = require('./commands/backend-core.js');
6
7
  var index = require('./config/index.js');
7
8
 
8
9
 
@@ -10,5 +11,10 @@ var index = require('./config/index.js');
10
11
  exports.checkTranslations = checkTranslations.checkTranslations;
11
12
  exports.cleanTranslations = cleanTranslations.cleanTranslations;
12
13
  exports.generateBlogIndex = generateBlogIndex.generateBlogIndex;
14
+ exports.listBackendCoreRoutes = backendCore.listBackendCoreRoutes;
15
+ exports.registerBackendCoreCommands = backendCore.registerBackendCoreCommands;
16
+ exports.syncBackendCoreMigrations = backendCore.syncBackendCoreMigrations;
17
+ exports.syncBackendCorePrisma = backendCore.syncBackendCorePrisma;
18
+ exports.syncBackendCoreRoutes = backendCore.syncBackendCoreRoutes;
13
19
  exports.loadConfig = index.loadConfig;
14
20
  exports.validateConfig = index.validateConfig;
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  export { checkTranslations } from './commands/check-translations.mjs';
2
2
  export { cleanTranslations } from './commands/clean-translations.mjs';
3
3
  export { generateBlogIndex } from './commands/generate-blog-index.mjs';
4
+ export { listBackendCoreRoutes, registerBackendCoreCommands, syncBackendCoreMigrations, syncBackendCorePrisma, syncBackendCoreRoutes } from './commands/backend-core.mjs';
4
5
  export { loadConfig, validateConfig } from './config/index.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/dev-scripts",
3
- "version": "10.0.0",
3
+ "version": "10.2.0",
4
4
  "description": "Development scripts for multilingual projects",
5
5
  "main": "dist/index.js",
6
6
  "bin": {