swallowkit 1.0.0-beta.21 → 1.0.0-beta.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/README.ja.md +4 -4
- package/README.md +4 -4
- package/dist/cli/commands/dev.d.ts +11 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +80 -7
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +17 -18
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +0 -3
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +3 -172
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +37 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/project/validation.js +2 -2
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +5 -6
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
- package/dist/core/scaffold/native-schema-generator.js +667 -0
- package/dist/core/scaffold/native-schema-generator.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/dev.test.ts +53 -1
- package/src/__tests__/model-parser.test.ts +44 -64
- package/src/__tests__/scaffold.test.ts +54 -26
- package/src/cli/commands/dev.ts +101 -8
- package/src/cli/commands/init.ts +26 -19
- package/src/cli/commands/scaffold.ts +3 -213
- package/src/cli/index.ts +4 -1
- package/src/core/project/validation.ts +2 -2
- package/src/core/scaffold/model-parser.ts +7 -7
- package/src/core/scaffold/native-schema-generator.ts +769 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import * as path from "path";
|
|
8
|
-
import {
|
|
8
|
+
import { spawnSync } from "child_process";
|
|
9
9
|
import { getBackendLanguage, getConnectorDefinition, getAuthConfig, ensureSwallowKitProject } from "../../core/config";
|
|
10
10
|
import { ModelInfo, parseModelFile, toKebabCase, toPascalCase, toCamelCase } from "../../core/scaffold/model-parser";
|
|
11
11
|
import {
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
generateApiConnectorFunctionPython,
|
|
23
23
|
} from "../../core/scaffold/connector-functions-generator";
|
|
24
24
|
import { generateCompactBFFRoutes, generateBFFCallFunction, generateConnectorBFFRoutes } from "../../core/scaffold/nextjs-generator";
|
|
25
|
-
import { generateOpenApiDocument } from "../../core/scaffold/openapi-generator";
|
|
26
25
|
import {
|
|
27
26
|
generateListPage,
|
|
28
27
|
generateDetailPage,
|
|
@@ -31,6 +30,7 @@ import {
|
|
|
31
30
|
generateEditPage,
|
|
32
31
|
UIAuthOptions,
|
|
33
32
|
} from "../../core/scaffold/ui-generator";
|
|
33
|
+
import { generateLanguageSchemaArtifacts } from "../../core/scaffold/native-schema-generator";
|
|
34
34
|
import { detectFromProject, getCommands } from "../../utils/package-manager";
|
|
35
35
|
import {
|
|
36
36
|
BackendLanguage,
|
|
@@ -171,7 +171,7 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
171
171
|
console.log(" 3. Navigate to the model from the homepage menu");
|
|
172
172
|
}
|
|
173
173
|
if (backendLanguage !== "typescript") {
|
|
174
|
-
console.log(` ${options.apiOnly ? "2" : "4"}. Review generated OpenAPI assets in ${functionsDir}/openapi/ and ${functionsDir}/generated/`);
|
|
174
|
+
console.log(` ${options.apiOnly ? "2" : "4"}. Review generated OpenAPI export and native schema assets in ${functionsDir}/openapi/ and ${functionsDir}/generated/`);
|
|
175
175
|
}
|
|
176
176
|
console.log(
|
|
177
177
|
` ${options.apiOnly ? (backendLanguage === "typescript" ? "2" : "3") : (backendLanguage === "typescript" ? "4" : "5")}. Ensure BACKEND_FUNCTIONS_BASE_URL is set in your .env.local file`
|
|
@@ -636,216 +636,6 @@ function describeFunctionsOutputPath(functionsDir: string, backendLanguage: Back
|
|
|
636
636
|
return `${functionsDir}/blueprints/`;
|
|
637
637
|
}
|
|
638
638
|
|
|
639
|
-
async function generateLanguageSchemaArtifacts(
|
|
640
|
-
models: ModelInfo[],
|
|
641
|
-
rootModel: ModelInfo,
|
|
642
|
-
functionsDir: string,
|
|
643
|
-
backendLanguage: Exclude<BackendLanguage, "typescript">
|
|
644
|
-
): Promise<void> {
|
|
645
|
-
console.log("\n🧬 Generating OpenAPI-backed schema artifacts...");
|
|
646
|
-
|
|
647
|
-
const openApiDir = path.join(process.cwd(), functionsDir, "openapi");
|
|
648
|
-
fs.mkdirSync(openApiDir, { recursive: true });
|
|
649
|
-
|
|
650
|
-
const specPath = path.join(openApiDir, `${toKebabCase(rootModel.name)}.openapi.json`);
|
|
651
|
-
fs.writeFileSync(specPath, generateOpenApiDocument(models, rootModel), "utf-8");
|
|
652
|
-
console.log(`✅ Created: ${specPath}`);
|
|
653
|
-
|
|
654
|
-
const outputDir = path.join(
|
|
655
|
-
process.cwd(),
|
|
656
|
-
functionsDir,
|
|
657
|
-
"generated",
|
|
658
|
-
backendLanguage === "csharp" ? "csharp-models" : "python-models"
|
|
659
|
-
);
|
|
660
|
-
|
|
661
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
662
|
-
await runOpenApiGenerator(specPath, outputDir, backendLanguage);
|
|
663
|
-
if (backendLanguage === "csharp") {
|
|
664
|
-
pruneGeneratedCSharpArtifacts(outputDir);
|
|
665
|
-
}
|
|
666
|
-
console.log(`✅ Generated ${backendLanguage} schema assets: ${outputDir}`);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* OpenAPI Generator の実行前に Java バージョンを確認する。
|
|
671
|
-
* Java 11 未満の場合は明確なエラーメッセージを表示して処理を中断する。
|
|
672
|
-
*/
|
|
673
|
-
function checkJavaVersion(): void {
|
|
674
|
-
try {
|
|
675
|
-
const result = spawnSync("java", ["-version"], { encoding: "utf8" });
|
|
676
|
-
// java -version は stderr に出力する
|
|
677
|
-
const versionOutput = (result.stderr || "") + (result.stdout || "");
|
|
678
|
-
const versionMatch = versionOutput.match(/version "(\d+)(?:\.(\d+))?/);
|
|
679
|
-
if (!versionMatch) return;
|
|
680
|
-
|
|
681
|
-
const major = parseInt(versionMatch[1]);
|
|
682
|
-
// Java 8 以前は "1.8" 形式、Java 9 以降は "17" 形式
|
|
683
|
-
const effectiveMajor = major === 1 ? parseInt(versionMatch[2] ?? "0") : major;
|
|
684
|
-
|
|
685
|
-
if (effectiveMajor < 11) {
|
|
686
|
-
const versionStr = versionOutput.match(/version "([^"]+)"/)?.[1] ?? "unknown";
|
|
687
|
-
console.error(`\n❌ OpenAPI Generator requires Java 11 or later.`);
|
|
688
|
-
console.error(` Detected: Java ${versionStr}`);
|
|
689
|
-
if (process.env.JAVA_HOME) {
|
|
690
|
-
console.error(` Current JAVA_HOME: ${process.env.JAVA_HOME}`);
|
|
691
|
-
}
|
|
692
|
-
console.error(` Please set JAVA_HOME to a Java 11+ installation and retry.`);
|
|
693
|
-
console.error(` Example (Windows): $env:JAVA_HOME = "C:\\path\\to\\jdk-17"`);
|
|
694
|
-
process.exit(1);
|
|
695
|
-
}
|
|
696
|
-
} catch {
|
|
697
|
-
// java コマンドが見つからない場合は OpenAPI Generator 自身がエラーを出す
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
async function runOpenApiGenerator(
|
|
702
|
-
specPath: string,
|
|
703
|
-
outputDir: string,
|
|
704
|
-
backendLanguage: Exclude<BackendLanguage, "typescript">
|
|
705
|
-
): Promise<void> {
|
|
706
|
-
checkJavaVersion();
|
|
707
|
-
const pm = detectFromProject();
|
|
708
|
-
const command = pm === "pnpm" ? "pnpm" : "npx";
|
|
709
|
-
const args = pm === "pnpm"
|
|
710
|
-
? ["exec", "openapi-generator-cli", ...getOpenApiGeneratorArgs(specPath, outputDir, backendLanguage)]
|
|
711
|
-
: ["openapi-generator-cli", ...getOpenApiGeneratorArgs(specPath, outputDir, backendLanguage)];
|
|
712
|
-
|
|
713
|
-
await new Promise<void>((resolve, reject) => {
|
|
714
|
-
const child = spawn(command, args, {
|
|
715
|
-
cwd: process.cwd(),
|
|
716
|
-
shell: true,
|
|
717
|
-
stdio: getMachineAwareStdio(),
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
child.on("close", (code) => {
|
|
721
|
-
if (code === 0) {
|
|
722
|
-
resolve();
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
reject(new Error(`OpenAPI generator exited with code ${code}`));
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
child.on("error", reject);
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
export function getOpenApiGeneratorArgs(
|
|
734
|
-
specPath: string,
|
|
735
|
-
outputDir: string,
|
|
736
|
-
backendLanguage: Exclude<BackendLanguage, "typescript">
|
|
737
|
-
): string[] {
|
|
738
|
-
const globalProperty = backendLanguage === "csharp"
|
|
739
|
-
? "models,apis=false,modelDocs=false,modelTests=false"
|
|
740
|
-
: "models,apis=false,supportingFiles=false,modelDocs=false,modelTests=false";
|
|
741
|
-
|
|
742
|
-
const baseArgs = [
|
|
743
|
-
"generate",
|
|
744
|
-
"-i",
|
|
745
|
-
specPath,
|
|
746
|
-
"-o",
|
|
747
|
-
outputDir,
|
|
748
|
-
"--global-property",
|
|
749
|
-
globalProperty,
|
|
750
|
-
];
|
|
751
|
-
|
|
752
|
-
if (backendLanguage === "csharp") {
|
|
753
|
-
return [
|
|
754
|
-
...baseArgs,
|
|
755
|
-
"-g",
|
|
756
|
-
"csharp",
|
|
757
|
-
"--additional-properties",
|
|
758
|
-
"packageName=SwallowKitBackendModels,targetFramework=net8.0,nullableReferenceTypes=true",
|
|
759
|
-
];
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
return [
|
|
763
|
-
...baseArgs,
|
|
764
|
-
"-g",
|
|
765
|
-
"python",
|
|
766
|
-
"--additional-properties",
|
|
767
|
-
"packageName=backend_models,projectName=backend-models",
|
|
768
|
-
];
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
export function getCSharpSchemaArtifactPruneTargets(outputDir: string): string[] {
|
|
772
|
-
return [
|
|
773
|
-
path.join(outputDir, "src", "SwallowKitBackendModels.Test"),
|
|
774
|
-
path.join(outputDir, "src", "SwallowKitBackendModels", "Api"),
|
|
775
|
-
path.join(outputDir, "src", "SwallowKitBackendModels", "Extensions"),
|
|
776
|
-
];
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function pruneGeneratedCSharpArtifacts(outputDir: string): void {
|
|
780
|
-
for (const target of getCSharpSchemaArtifactPruneTargets(outputDir)) {
|
|
781
|
-
if (fs.existsSync(target)) {
|
|
782
|
-
fs.rmSync(target, { recursive: true, force: true });
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
const clientDir = path.join(outputDir, "src", "SwallowKitBackendModels", "Client");
|
|
787
|
-
if (!fs.existsSync(clientDir)) {
|
|
788
|
-
// --global-property models ではClient/が生成されないため、
|
|
789
|
-
// モデルが依存する最小限の Option<T> を自前で作成する
|
|
790
|
-
fs.mkdirSync(clientDir, { recursive: true });
|
|
791
|
-
fs.writeFileSync(
|
|
792
|
-
path.join(clientDir, "Option.cs"),
|
|
793
|
-
generateMinimalOptionCs(),
|
|
794
|
-
"utf-8"
|
|
795
|
-
);
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
for (const entry of fs.readdirSync(clientDir, { withFileTypes: true })) {
|
|
800
|
-
if (!entry.isFile() || entry.name === "Option.cs") {
|
|
801
|
-
continue;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
fs.rmSync(path.join(clientDir, entry.name), { force: true });
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
/**
|
|
809
|
-
* OpenAPI Generator の csharp テンプレートが生成するモデルは Option<T> に依存するが、
|
|
810
|
-
* supportingFiles を除外しているため Client/Option.cs が生成されない。
|
|
811
|
-
* Polly 等の不要な依存を避けつつモデルをコンパイル可能にするため、最小限の Option<T> を提供する。
|
|
812
|
-
*/
|
|
813
|
-
function generateMinimalOptionCs(): string {
|
|
814
|
-
return `// <auto-generated>
|
|
815
|
-
// Minimal Option<T> for OpenAPI Generator model compatibility.
|
|
816
|
-
// Full client supporting files are excluded to avoid Polly version conflicts.
|
|
817
|
-
// </auto-generated>
|
|
818
|
-
|
|
819
|
-
#nullable enable
|
|
820
|
-
|
|
821
|
-
namespace SwallowKitBackendModels.Client
|
|
822
|
-
{
|
|
823
|
-
/// <summary>
|
|
824
|
-
/// A wrapper for nullable/optional properties generated by OpenAPI Generator.
|
|
825
|
-
/// Tracks whether a value has been explicitly set (distinguishing null from absent).
|
|
826
|
-
/// </summary>
|
|
827
|
-
public readonly struct Option<TValue>
|
|
828
|
-
{
|
|
829
|
-
/// <summary>Whether this option has been explicitly set.</summary>
|
|
830
|
-
public bool IsSet { get; }
|
|
831
|
-
|
|
832
|
-
/// <summary>The contained value (may be default if not set).</summary>
|
|
833
|
-
public TValue Value { get; }
|
|
834
|
-
|
|
835
|
-
/// <summary>Create an Option with an explicit value.</summary>
|
|
836
|
-
public Option(TValue value)
|
|
837
|
-
{
|
|
838
|
-
IsSet = true;
|
|
839
|
-
Value = value;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/// <summary>Implicit conversion from Option to its inner value.</summary>
|
|
843
|
-
public static implicit operator TValue(Option<TValue> option) => option.Value;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
`;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
639
|
function updatePythonFunctionRegistrations(functionAppPath: string, registration: string): void {
|
|
850
640
|
if (!fs.existsSync(functionAppPath)) {
|
|
851
641
|
throw new Error(`Python Functions entrypoint not found: ${functionAppPath}`);
|
package/src/cli/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import * as path from "path";
|
|
4
6
|
import { Command } from "commander";
|
|
5
7
|
import { initCommand, devCommand, devSeedsCommand, scaffoldCommand, createModelCommand } from "./commands";
|
|
6
8
|
import { provisionCommand } from "./commands/provision";
|
|
@@ -8,7 +10,8 @@ import { addConnectorCommand } from "./commands/add-connector";
|
|
|
8
10
|
import { addAuthCommand } from "./commands/add-auth";
|
|
9
11
|
import { isMachineCommand, runMachineCli } from "../machine";
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
const packageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
14
|
+
export const CLI_VERSION = JSON.parse(readFileSync(packageJsonPath, "utf8")).version as string;
|
|
12
15
|
|
|
13
16
|
const DEV_OPTION_ARITY = new Map<string, 0 | 1>([
|
|
14
17
|
["-p", 1],
|
|
@@ -102,9 +102,9 @@ function validateGeneratedArtifacts(manifest: ProjectManifest, violations: Proje
|
|
|
102
102
|
pushViolation(violations, {
|
|
103
103
|
code: "missing-openapi-artifacts",
|
|
104
104
|
severity: "warning",
|
|
105
|
-
message: `No OpenAPI schema artifacts were found for ${manifest.backendLanguage} backend generation.`,
|
|
105
|
+
message: `No OpenAPI export or native schema artifacts were found for ${manifest.backendLanguage} backend generation.`,
|
|
106
106
|
location: { path: "functions/openapi" },
|
|
107
|
-
suggestedFix: "Run scaffold to regenerate the OpenAPI
|
|
107
|
+
suggestedFix: "Run scaffold to regenerate the OpenAPI export and native backend schema artifacts.",
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as fs from "fs";
|
|
6
|
-
import {
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
|
|
9
9
|
import { ModelConnectorConfig, ModelAuthPolicy } from "../../types";
|
|
10
|
-
import { detectFromProject, getCommands } from "../../utils/package-manager";
|
|
11
10
|
|
|
12
11
|
export interface ModelInfo {
|
|
13
12
|
name: string; // モデル名(例: "Todo")
|
|
@@ -377,6 +376,10 @@ function inlineLocalDeps(
|
|
|
377
376
|
return [...transitiveParts, content.trim()].filter(Boolean).join('\n\n');
|
|
378
377
|
}
|
|
379
378
|
|
|
379
|
+
function resolveBundledTsxCliPath(): string {
|
|
380
|
+
return require.resolve("tsx/cli");
|
|
381
|
+
}
|
|
382
|
+
|
|
380
383
|
/**
|
|
381
384
|
* Zodスキーマから動的にフィールド情報を抽出
|
|
382
385
|
*/
|
|
@@ -543,11 +546,8 @@ if (isObject) {
|
|
|
543
546
|
fs.writeFileSync(tempScript, scriptCode, 'utf8');
|
|
544
547
|
|
|
545
548
|
try {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const pm = detectFromProject(projectRoot);
|
|
549
|
-
const pmCmd = getCommands(pm);
|
|
550
|
-
const result = execSync(`${pmCmd.exec} tsx "${tempScript}"`, {
|
|
549
|
+
const tsxCliPath = resolveBundledTsxCliPath();
|
|
550
|
+
const result = execFileSync(process.execPath, [tsxCliPath, tempScript], {
|
|
551
551
|
encoding: 'utf8',
|
|
552
552
|
cwd: projectRoot,
|
|
553
553
|
});
|