swallowkit 1.0.0-beta.56 → 1.0.0-beta.57
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/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +8 -6
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/core/operations/file-session.d.ts +3 -0
- package/dist/core/operations/file-session.d.ts.map +1 -1
- package/dist/core/operations/file-session.js +8 -0
- package/dist/core/operations/file-session.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +1 -9
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +7 -1
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +104 -47
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/plan-apply.test.ts +167 -0
- package/src/__tests__/scaffold.test.ts +2 -2
- package/src/cli/commands/scaffold.ts +15 -8
- package/src/core/operations/file-session.ts +12 -0
- package/src/core/operations/scaffold-plan.ts +2 -11
- package/src/core/scaffold/native-schema-generator.ts +152 -70
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import { spawn, spawnSync } from "child_process";
|
|
4
5
|
import { BackendLanguage } from "../../types";
|
|
6
|
+
import { FileOperationSession } from "../operations/file-session";
|
|
5
7
|
import { ModelInfo, toKebabCase } from "./model-parser";
|
|
6
8
|
import { generateOpenApiDocument } from "./openapi-generator";
|
|
7
9
|
import {
|
|
@@ -621,23 +623,47 @@ function mergePythonInitSource(existingSource: string | undefined, generatedSour
|
|
|
621
623
|
return `${normalizedExisting}${missingLines.join("\n")}\n`;
|
|
622
624
|
}
|
|
623
625
|
|
|
624
|
-
function writeMergedPythonInitFile(
|
|
625
|
-
|
|
626
|
-
|
|
626
|
+
function writeMergedPythonInitFile(
|
|
627
|
+
filePath: string,
|
|
628
|
+
generatedSource: string,
|
|
629
|
+
session: FileOperationSession,
|
|
630
|
+
sourceModel: string
|
|
631
|
+
): void {
|
|
632
|
+
const existingSource = session.fileExists(filePath) ? session.readFile(filePath) : undefined;
|
|
633
|
+
session.writeFile(filePath, mergePythonInitSource(existingSource, generatedSource), {
|
|
634
|
+
ownership: "extension-point",
|
|
635
|
+
generator: "native-schema",
|
|
636
|
+
sourceModel,
|
|
637
|
+
});
|
|
627
638
|
}
|
|
628
639
|
|
|
629
|
-
function ensureCSharpCodegenProjectFiles(
|
|
640
|
+
function ensureCSharpCodegenProjectFiles(
|
|
641
|
+
functionsRoot: string,
|
|
642
|
+
session: FileOperationSession,
|
|
643
|
+
sourceModel: string
|
|
644
|
+
): void {
|
|
630
645
|
const toolManifestPath = path.join(functionsRoot, ".config", "dotnet-tools.json");
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
646
|
+
if (!session.fileExists(toolManifestPath)) {
|
|
647
|
+
session.writeFile(toolManifestPath, buildCSharpCodegenToolManifestSource(), {
|
|
648
|
+
ownership: "generated-once",
|
|
649
|
+
generator: "native-schema",
|
|
650
|
+
sourceModel,
|
|
651
|
+
});
|
|
634
652
|
}
|
|
635
653
|
}
|
|
636
654
|
|
|
637
|
-
function ensurePythonCodegenProjectFiles(
|
|
655
|
+
function ensurePythonCodegenProjectFiles(
|
|
656
|
+
functionsRoot: string,
|
|
657
|
+
session: FileOperationSession,
|
|
658
|
+
sourceModel: string
|
|
659
|
+
): string {
|
|
638
660
|
const requirementsPath = path.join(functionsRoot, "requirements.codegen.txt");
|
|
639
|
-
if (!
|
|
640
|
-
|
|
661
|
+
if (!session.fileExists(requirementsPath)) {
|
|
662
|
+
session.writeFile(requirementsPath, buildPythonCodegenRequirementsSource(), {
|
|
663
|
+
ownership: "generated-once",
|
|
664
|
+
generator: "native-schema",
|
|
665
|
+
sourceModel,
|
|
666
|
+
});
|
|
641
667
|
}
|
|
642
668
|
return requirementsPath;
|
|
643
669
|
}
|
|
@@ -676,8 +702,7 @@ async function ensureProjectLocalUvCommand(projectRoot: string): Promise<{ comma
|
|
|
676
702
|
return { command: localUvExecutable, env: uvEnv };
|
|
677
703
|
}
|
|
678
704
|
|
|
679
|
-
async function ensurePythonCodegenEnvironment(functionsRoot: string): Promise<string> {
|
|
680
|
-
const requirementsPath = ensurePythonCodegenProjectFiles(functionsRoot);
|
|
705
|
+
async function ensurePythonCodegenEnvironment(functionsRoot: string, requirementsPath: string): Promise<string> {
|
|
681
706
|
const projectRoot = getPythonProjectRoot(functionsRoot);
|
|
682
707
|
const { command: uvCommand, env: uvEnv } = await ensureProjectLocalUvCommand(projectRoot);
|
|
683
708
|
const venvDir = path.join(functionsRoot, ".codegen-venv");
|
|
@@ -711,79 +736,125 @@ async function ensurePythonCodegenEnvironment(functionsRoot: string): Promise<st
|
|
|
711
736
|
return venvPython;
|
|
712
737
|
}
|
|
713
738
|
|
|
714
|
-
|
|
739
|
+
/**
|
|
740
|
+
* 外部 codegen toolchain の事前検証。プロジェクトには一切書き込まず、
|
|
741
|
+
* OS 一時ディレクトリ上で NSwag / datamodel-code-generator を実行して失敗を早期検出する。
|
|
742
|
+
*/
|
|
743
|
+
export async function validateNativeSchemaToolchain(
|
|
715
744
|
models: ModelInfo[],
|
|
716
|
-
|
|
717
|
-
|
|
745
|
+
rootModel: ModelInfo,
|
|
746
|
+
backendLanguage: Exclude<BackendLanguage, "typescript">,
|
|
718
747
|
functionsRoot: string
|
|
719
748
|
): Promise<void> {
|
|
720
|
-
|
|
749
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "swallowkit-native-codegen-"));
|
|
750
|
+
try {
|
|
751
|
+
const specPath = path.join(tempDir, `${toKebabCase(rootModel.name)}.openapi.json`);
|
|
752
|
+
fs.writeFileSync(specPath, generateOpenApiDocument(models, rootModel), "utf-8");
|
|
753
|
+
|
|
754
|
+
if (backendLanguage === "csharp") {
|
|
755
|
+
if (!canRun("dotnet", ["--version"], tempDir)) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
"The .NET SDK is required to generate C# backend schema assets.\n" +
|
|
758
|
+
"Install the .NET 8 SDK and retry."
|
|
759
|
+
);
|
|
760
|
+
}
|
|
721
761
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
762
|
+
// プロジェクトの tool manifest(バージョン固定)があればそれを尊重し、なければ一時 manifest で検証する
|
|
763
|
+
const projectManifestPath = path.join(functionsRoot, ".config", "dotnet-tools.json");
|
|
764
|
+
let toolCwd = functionsRoot;
|
|
765
|
+
if (!fs.existsSync(projectManifestPath)) {
|
|
766
|
+
fs.mkdirSync(path.join(tempDir, ".config"), { recursive: true });
|
|
767
|
+
fs.writeFileSync(path.join(tempDir, ".config", "dotnet-tools.json"), buildCSharpCodegenToolManifestSource(), "utf-8");
|
|
768
|
+
toolCwd = tempDir;
|
|
769
|
+
}
|
|
728
770
|
|
|
729
|
-
|
|
730
|
-
|
|
771
|
+
await runCommand("dotnet", ["tool", "restore"], toolCwd, "Failed to restore the NSwag dotnet tool.");
|
|
772
|
+
await runCommand(
|
|
773
|
+
"dotnet",
|
|
774
|
+
getCSharpNativeGeneratorArgs(specPath, path.join(tempDir, "Contracts.cs")),
|
|
775
|
+
toolCwd,
|
|
776
|
+
"NSwag failed to generate C# backend schema assets."
|
|
777
|
+
);
|
|
778
|
+
} else {
|
|
779
|
+
const projectRequirementsPath = path.join(functionsRoot, "requirements.codegen.txt");
|
|
780
|
+
let requirementsPath = projectRequirementsPath;
|
|
781
|
+
if (!fs.existsSync(projectRequirementsPath)) {
|
|
782
|
+
requirementsPath = path.join(tempDir, "requirements.codegen.txt");
|
|
783
|
+
fs.writeFileSync(requirementsPath, buildPythonCodegenRequirementsSource(), "utf-8");
|
|
784
|
+
}
|
|
731
785
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
786
|
+
// .codegen-venv は environment cache であり managed artifact ではない(既存挙動どおり functionsRoot 配下)
|
|
787
|
+
fs.mkdirSync(functionsRoot, { recursive: true });
|
|
788
|
+
const pythonExecutable = await ensurePythonCodegenEnvironment(functionsRoot, requirementsPath);
|
|
789
|
+
await runCommand(
|
|
790
|
+
pythonExecutable,
|
|
791
|
+
getPythonNativeGeneratorArgs(specPath, path.join(tempDir, "models.py")),
|
|
792
|
+
functionsRoot,
|
|
793
|
+
"datamodel-code-generator failed to generate Python backend schema assets."
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
} finally {
|
|
797
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
798
|
+
}
|
|
799
|
+
}
|
|
744
800
|
|
|
745
|
-
|
|
801
|
+
async function generateCSharpSchemaArtifacts(
|
|
802
|
+
models: ModelInfo[],
|
|
803
|
+
outputDir: string,
|
|
804
|
+
functionsRoot: string,
|
|
805
|
+
session: FileOperationSession,
|
|
806
|
+
sourceModel: string
|
|
807
|
+
): Promise<void> {
|
|
808
|
+
ensureCSharpCodegenProjectFiles(functionsRoot, session, sourceModel);
|
|
746
809
|
|
|
747
810
|
const optionPath = getCSharpSchemaOptionPath(outputDir);
|
|
748
|
-
|
|
749
|
-
|
|
811
|
+
session.writeFile(optionPath, generateLegacyCompatibleOptionSource(), {
|
|
812
|
+
ownership: "managed",
|
|
813
|
+
generator: "native-schema",
|
|
814
|
+
sourceModel,
|
|
815
|
+
});
|
|
750
816
|
|
|
751
817
|
for (const model of models) {
|
|
752
818
|
const modelPath = getCSharpSchemaModelPath(outputDir, model.name);
|
|
753
|
-
|
|
754
|
-
|
|
819
|
+
session.writeFile(modelPath, generateLegacyCompatibleCSharpModelSource(model), {
|
|
820
|
+
ownership: "managed",
|
|
821
|
+
generator: "native-schema",
|
|
822
|
+
sourceModel,
|
|
823
|
+
});
|
|
755
824
|
}
|
|
756
825
|
}
|
|
757
826
|
|
|
758
827
|
async function generatePythonSchemaArtifacts(
|
|
759
828
|
models: ModelInfo[],
|
|
760
|
-
specPath: string,
|
|
761
829
|
outputDir: string,
|
|
762
|
-
functionsRoot: string
|
|
830
|
+
functionsRoot: string,
|
|
831
|
+
session: FileOperationSession,
|
|
832
|
+
sourceModel: string
|
|
763
833
|
): Promise<void> {
|
|
764
|
-
|
|
765
|
-
const tempModelsPath = path.join(outputDir, ".native-temp", "models.py");
|
|
766
|
-
fs.mkdirSync(path.dirname(tempModelsPath), { recursive: true });
|
|
767
|
-
|
|
768
|
-
await runCommand(
|
|
769
|
-
pythonExecutable,
|
|
770
|
-
getPythonNativeGeneratorArgs(specPath, tempModelsPath),
|
|
771
|
-
functionsRoot,
|
|
772
|
-
"datamodel-code-generator failed to generate Python backend schema assets."
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
fs.rmSync(path.dirname(tempModelsPath), { recursive: true, force: true });
|
|
834
|
+
ensurePythonCodegenProjectFiles(functionsRoot, session, sourceModel);
|
|
776
835
|
|
|
777
836
|
const packageRoot = path.join(outputDir, "backend_models");
|
|
778
837
|
const modelsRoot = path.join(packageRoot, "models");
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
838
|
+
writeMergedPythonInitFile(
|
|
839
|
+
path.join(packageRoot, "__init__.py"),
|
|
840
|
+
buildGeneratedPythonPackageInitSource(models),
|
|
841
|
+
session,
|
|
842
|
+
sourceModel
|
|
843
|
+
);
|
|
844
|
+
writeMergedPythonInitFile(
|
|
845
|
+
path.join(modelsRoot, "__init__.py"),
|
|
846
|
+
buildGeneratedPythonModelsInitSource(models),
|
|
847
|
+
session,
|
|
848
|
+
sourceModel
|
|
849
|
+
);
|
|
782
850
|
|
|
783
851
|
for (const model of models) {
|
|
784
852
|
const modelPath = getPythonSchemaModelPath(outputDir, model.name);
|
|
785
|
-
|
|
786
|
-
|
|
853
|
+
session.writeFile(modelPath, generateLegacyCompatiblePythonModelSource(model), {
|
|
854
|
+
ownership: "managed",
|
|
855
|
+
generator: "native-schema",
|
|
856
|
+
sourceModel,
|
|
857
|
+
});
|
|
787
858
|
}
|
|
788
859
|
}
|
|
789
860
|
|
|
@@ -791,18 +862,27 @@ export async function generateLanguageSchemaArtifacts(
|
|
|
791
862
|
models: ModelInfo[],
|
|
792
863
|
rootModel: ModelInfo,
|
|
793
864
|
functionsDir: string,
|
|
794
|
-
backendLanguage: Exclude<BackendLanguage, "typescript"
|
|
865
|
+
backendLanguage: Exclude<BackendLanguage, "typescript">,
|
|
866
|
+
session: FileOperationSession = new FileOperationSession("commit")
|
|
795
867
|
): Promise<void> {
|
|
796
|
-
|
|
868
|
+
const planning = session.mode === "collect";
|
|
869
|
+
console.log(
|
|
870
|
+
planning
|
|
871
|
+
? "\n\ud83e\uddec Planning OpenAPI export and native schema assets..."
|
|
872
|
+
: "\n\ud83e\uddec Generating OpenAPI export and native schema assets..."
|
|
873
|
+
);
|
|
797
874
|
|
|
798
875
|
const projectRoot = process.cwd();
|
|
799
876
|
const functionsRoot = path.join(projectRoot, functionsDir);
|
|
800
877
|
const openApiDir = path.join(functionsRoot, "openapi");
|
|
801
|
-
fs.mkdirSync(openApiDir, { recursive: true });
|
|
802
878
|
|
|
803
879
|
const specPath = path.join(openApiDir, `${toKebabCase(rootModel.name)}.openapi.json`);
|
|
804
|
-
|
|
805
|
-
|
|
880
|
+
session.writeFile(specPath, generateOpenApiDocument(models, rootModel), {
|
|
881
|
+
ownership: "managed",
|
|
882
|
+
generator: "native-schema",
|
|
883
|
+
sourceModel: rootModel.name,
|
|
884
|
+
});
|
|
885
|
+
console.log(planning ? `\ud83d\udccb Will create: ${specPath}` : `\u2705 Created: ${specPath}`);
|
|
806
886
|
|
|
807
887
|
const outputDir = path.join(
|
|
808
888
|
functionsRoot,
|
|
@@ -810,13 +890,15 @@ export async function generateLanguageSchemaArtifacts(
|
|
|
810
890
|
backendLanguage === "csharp" ? "csharp-models" : "python-models"
|
|
811
891
|
);
|
|
812
892
|
|
|
813
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
814
|
-
|
|
815
893
|
if (backendLanguage === "csharp") {
|
|
816
|
-
await generateCSharpSchemaArtifacts(models,
|
|
894
|
+
await generateCSharpSchemaArtifacts(models, outputDir, functionsRoot, session, rootModel.name);
|
|
817
895
|
} else {
|
|
818
|
-
await generatePythonSchemaArtifacts(models,
|
|
896
|
+
await generatePythonSchemaArtifacts(models, outputDir, functionsRoot, session, rootModel.name);
|
|
819
897
|
}
|
|
820
898
|
|
|
821
|
-
console.log(
|
|
899
|
+
console.log(
|
|
900
|
+
planning
|
|
901
|
+
? `\ud83d\udccb Will generate ${backendLanguage} schema assets: ${outputDir}`
|
|
902
|
+
: `\u2705 Generated ${backendLanguage} schema assets: ${outputDir}`
|
|
903
|
+
);
|
|
822
904
|
}
|