swallowkit 1.0.0-beta.5 → 1.0.0-beta.7
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 +21 -21
- package/README.ja.md +251 -242
- package/README.md +252 -243
- package/dist/__tests__/fixtures.d.ts +14 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures.js +85 -0
- package/dist/__tests__/fixtures.js.map +1 -0
- package/dist/cli/commands/create-model.js +14 -14
- package/dist/cli/commands/dev.d.ts +8 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +238 -30
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2507 -1664
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +3 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +281 -117
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +28 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/scaffold/functions-generator.d.ts +5 -0
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +649 -218
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +1 -1
- package/dist/core/scaffold/model-parser.js +99 -99
- package/dist/core/scaffold/nextjs-generator.js +181 -181
- package/dist/core/scaffold/openapi-generator.d.ts +3 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
- package/dist/core/scaffold/openapi-generator.js +190 -0
- package/dist/core/scaffold/openapi-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.js +656 -656
- package/dist/database/base-model.d.ts +3 -3
- package/dist/database/base-model.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +2 -1
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +14 -10
- package/dist/utils/package-manager.js.map +1 -1
- package/package.json +81 -74
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +445 -0
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +524 -0
- package/src/__tests__/config.test.ts +122 -0
- package/src/__tests__/dev.test.ts +42 -0
- package/src/__tests__/fixtures.ts +83 -0
- package/src/__tests__/functions-generator.test.ts +101 -0
- package/src/__tests__/init.test.ts +59 -0
- package/src/__tests__/nextjs-generator.test.ts +97 -0
- package/src/__tests__/openapi-generator.test.ts +43 -0
- package/src/__tests__/package-manager.test.ts +189 -0
- package/src/__tests__/scaffold.test.ts +39 -0
- package/src/__tests__/string-utils.test.ts +75 -0
- package/src/__tests__/ui-generator.test.ts +144 -0
- package/src/cli/commands/create-model.ts +141 -0
- package/src/cli/commands/dev.ts +794 -0
- package/src/cli/commands/index.ts +8 -0
- package/src/cli/commands/init.ts +3363 -0
- package/src/cli/commands/provision.ts +193 -0
- package/src/cli/commands/scaffold.ts +786 -0
- package/src/cli/index.ts +73 -0
- package/src/core/config.ts +244 -0
- package/src/core/scaffold/functions-generator.ts +674 -0
- package/src/core/scaffold/model-parser.ts +627 -0
- package/src/core/scaffold/nextjs-generator.ts +217 -0
- package/src/core/scaffold/openapi-generator.ts +212 -0
- package/src/core/scaffold/ui-generator.ts +945 -0
- package/src/database/base-model.ts +184 -0
- package/src/database/client.ts +140 -0
- package/src/database/repository.ts +104 -0
- package/src/database/runtime-check.ts +25 -0
- package/src/index.ts +27 -0
- package/src/types/index.ts +45 -0
- package/src/utils/package-manager.ts +229 -0
- package/dist/cli/commands/build.d.ts +0 -6
- package/dist/cli/commands/build.d.ts.map +0 -1
- package/dist/cli/commands/build.js +0 -177
- package/dist/cli/commands/build.js.map +0 -1
- package/dist/cli/commands/deploy.d.ts +0 -3
- package/dist/cli/commands/deploy.d.ts.map +0 -1
- package/dist/cli/commands/deploy.js +0 -147
- package/dist/cli/commands/deploy.js.map +0 -1
- package/dist/cli/commands/setup.d.ts +0 -6
- package/dist/cli/commands/setup.d.ts.map +0 -1
- package/dist/cli/commands/setup.js +0 -254
- package/dist/cli/commands/setup.js.map +0 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ModelInfo } from "../core/scaffold/model-parser";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* テスト用の基本的な ModelInfo フィクスチャ
|
|
5
|
+
*/
|
|
6
|
+
export function createBasicModelInfo(overrides?: Partial<ModelInfo>): ModelInfo {
|
|
7
|
+
return {
|
|
8
|
+
name: "Todo",
|
|
9
|
+
displayName: "Todo",
|
|
10
|
+
schemaName: "todoSchema",
|
|
11
|
+
filePath: "/models/todo.ts",
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: "id", type: "string", isOptional: false, isArray: false },
|
|
14
|
+
{ name: "title", type: "string", isOptional: false, isArray: false },
|
|
15
|
+
{ name: "description", type: "string", isOptional: true, isArray: false },
|
|
16
|
+
{ name: "completed", type: "boolean", isOptional: false, isArray: false },
|
|
17
|
+
{ name: "createdAt", type: "string", isOptional: false, isArray: false },
|
|
18
|
+
{ name: "updatedAt", type: "string", isOptional: false, isArray: false },
|
|
19
|
+
],
|
|
20
|
+
hasId: true,
|
|
21
|
+
hasCreatedAt: true,
|
|
22
|
+
hasUpdatedAt: true,
|
|
23
|
+
nestedSchemaRefs: [],
|
|
24
|
+
...overrides,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 外部キーを含む ModelInfo フィクスチャ
|
|
30
|
+
*/
|
|
31
|
+
export function createModelInfoWithForeignKey(): ModelInfo {
|
|
32
|
+
return createBasicModelInfo({
|
|
33
|
+
name: "Task",
|
|
34
|
+
displayName: "Task",
|
|
35
|
+
schemaName: "taskSchema",
|
|
36
|
+
filePath: "/models/task.ts",
|
|
37
|
+
fields: [
|
|
38
|
+
{ name: "id", type: "string", isOptional: false, isArray: false },
|
|
39
|
+
{ name: "title", type: "string", isOptional: false, isArray: false },
|
|
40
|
+
{
|
|
41
|
+
name: "categoryId",
|
|
42
|
+
type: "string",
|
|
43
|
+
isOptional: false,
|
|
44
|
+
isArray: false,
|
|
45
|
+
isForeignKey: true,
|
|
46
|
+
referencedModel: "Category",
|
|
47
|
+
},
|
|
48
|
+
{ name: "createdAt", type: "string", isOptional: false, isArray: false },
|
|
49
|
+
{ name: "updatedAt", type: "string", isOptional: false, isArray: false },
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* enum フィールドを含む ModelInfo フィクスチャ
|
|
56
|
+
*/
|
|
57
|
+
export function createModelInfoWithEnum(): ModelInfo {
|
|
58
|
+
return createBasicModelInfo({
|
|
59
|
+
name: "Issue",
|
|
60
|
+
displayName: "Issue",
|
|
61
|
+
schemaName: "issueSchema",
|
|
62
|
+
filePath: "/models/issue.ts",
|
|
63
|
+
fields: [
|
|
64
|
+
{ name: "id", type: "string", isOptional: false, isArray: false },
|
|
65
|
+
{ name: "title", type: "string", isOptional: false, isArray: false },
|
|
66
|
+
{
|
|
67
|
+
name: "status",
|
|
68
|
+
type: "string",
|
|
69
|
+
isOptional: false,
|
|
70
|
+
isArray: false,
|
|
71
|
+
enumValues: ["open", "in_progress", "closed"],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "priority",
|
|
75
|
+
type: "number",
|
|
76
|
+
isOptional: true,
|
|
77
|
+
isArray: false,
|
|
78
|
+
},
|
|
79
|
+
{ name: "createdAt", type: "string", isOptional: false, isArray: false },
|
|
80
|
+
{ name: "updatedAt", type: "string", isOptional: false, isArray: false },
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateCSharpAzureFunctionsCRUD,
|
|
3
|
+
generateCompactAzureFunctionsCRUD,
|
|
4
|
+
generatePythonAzureFunctionsCRUD,
|
|
5
|
+
} from "../core/scaffold/functions-generator";
|
|
6
|
+
import { createBasicModelInfo, createModelInfoWithEnum } from "./fixtures";
|
|
7
|
+
|
|
8
|
+
describe("generateCompactAzureFunctionsCRUD", () => {
|
|
9
|
+
it("generates correct CRUD code for a basic model", () => {
|
|
10
|
+
const model = createBasicModelInfo();
|
|
11
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
12
|
+
expect(code).toMatchSnapshot();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("generates correct CRUD code for a model with enum fields", () => {
|
|
16
|
+
const model = createModelInfoWithEnum();
|
|
17
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
18
|
+
expect(code).toMatchSnapshot();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("imports the correct schema from shared package", () => {
|
|
22
|
+
const model = createBasicModelInfo();
|
|
23
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
24
|
+
expect(code).toContain("import { todoSchema } from '@myapp/shared'");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("uses correct container name (PascalCase + s)", () => {
|
|
28
|
+
const model = createBasicModelInfo();
|
|
29
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
30
|
+
expect(code).toContain("const containerName = 'Todos'");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("registers correct route names (camelCase)", () => {
|
|
34
|
+
const model = createBasicModelInfo();
|
|
35
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
36
|
+
expect(code).toContain("'todo-get-all'");
|
|
37
|
+
expect(code).toContain("'todo-get-by-id'");
|
|
38
|
+
expect(code).toContain("'todo-create'");
|
|
39
|
+
expect(code).toContain("'todo-update'");
|
|
40
|
+
expect(code).toContain("'todo-delete'");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("generates all CRUD HTTP methods", () => {
|
|
44
|
+
const model = createBasicModelInfo();
|
|
45
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
46
|
+
expect(code).toContain("methods: ['GET']");
|
|
47
|
+
expect(code).toContain("methods: ['POST']");
|
|
48
|
+
expect(code).toContain("methods: ['PUT']");
|
|
49
|
+
expect(code).toContain("methods: ['DELETE']");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("uses correct route patterns", () => {
|
|
53
|
+
const model = createBasicModelInfo();
|
|
54
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
55
|
+
expect(code).toContain("route: 'todo'");
|
|
56
|
+
expect(code).toContain("route: 'todo/{id}'");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("handles multi-word model names correctly", () => {
|
|
60
|
+
const model = createBasicModelInfo({
|
|
61
|
+
name: "TodoItem",
|
|
62
|
+
schemaName: "todoItemSchema",
|
|
63
|
+
});
|
|
64
|
+
const code = generateCompactAzureFunctionsCRUD(model, "@myapp/shared");
|
|
65
|
+
expect(code).toContain("const containerName = 'TodoItems'");
|
|
66
|
+
expect(code).toContain("route: 'todoItem'");
|
|
67
|
+
expect(code).toContain("'todoItem-get-all'");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("generates C# Cosmos-backed CRUD handlers", () => {
|
|
71
|
+
const model = createBasicModelInfo();
|
|
72
|
+
const code = generateCSharpAzureFunctionsCRUD(model);
|
|
73
|
+
expect(code).toContain("public sealed class TodoFunctions");
|
|
74
|
+
expect(code).toContain('[Function("todoGetAll")]');
|
|
75
|
+
expect(code).toContain('Route = "todo/{id}"');
|
|
76
|
+
expect(code).toContain("CreateCosmosClient()");
|
|
77
|
+
expect(code).toContain('new CosmosClientOptions { ConnectionMode = ConnectionMode.Gateway }');
|
|
78
|
+
expect(code).toContain('endpoint.Contains("localhost:8081", StringComparison.OrdinalIgnoreCase)');
|
|
79
|
+
expect(code).toContain("container.ReadItemStreamAsync");
|
|
80
|
+
expect(code).toContain("JsonNode.Parse(document.RootElement.GetRawText())?.AsObject()");
|
|
81
|
+
expect(code).toContain("container.CreateItemStreamAsync");
|
|
82
|
+
expect(code).toContain("container.ReplaceItemStreamAsync");
|
|
83
|
+
expect(code).toContain("payload.ToJsonString()");
|
|
84
|
+
expect(code).toContain("container.DeleteItemAsync<JsonObject>");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("generates Python Cosmos-backed CRUD handlers", () => {
|
|
88
|
+
const model = createBasicModelInfo();
|
|
89
|
+
const generated = generatePythonAzureFunctionsCRUD(model);
|
|
90
|
+
expect(generated.registration).toContain("from blueprints.todo import bp as todo_bp");
|
|
91
|
+
expect(generated.registration).toContain("app.register_blueprint(todo_bp)");
|
|
92
|
+
expect(generated.blueprint).toContain('@bp.route(route="todo", methods=["GET"])');
|
|
93
|
+
expect(generated.blueprint).toContain("def todo_create");
|
|
94
|
+
expect(generated.blueprint).toContain("from azure.cosmos import CosmosClient, exceptions");
|
|
95
|
+
expect(generated.blueprint).toContain("container.query_items");
|
|
96
|
+
expect(generated.blueprint).toContain("container.read_item");
|
|
97
|
+
expect(generated.blueprint).toContain("container.create_item");
|
|
98
|
+
expect(generated.blueprint).toContain("container.replace_item");
|
|
99
|
+
expect(generated.blueprint).toContain("container.delete_item");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCSharpFunctionsProgramSource,
|
|
3
|
+
buildCSharpFunctionsProjectSource,
|
|
4
|
+
injectSwallowKitNextConfig,
|
|
5
|
+
} from "../cli/commands/init";
|
|
6
|
+
|
|
7
|
+
describe("injectSwallowKitNextConfig", () => {
|
|
8
|
+
it("adds standalone settings without deprecated experimental options", () => {
|
|
9
|
+
const original = `import type { NextConfig } from "next";
|
|
10
|
+
|
|
11
|
+
const nextConfig: NextConfig = {
|
|
12
|
+
/* config options here */
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default nextConfig;
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const updated = injectSwallowKitNextConfig(original, "sample-app");
|
|
19
|
+
|
|
20
|
+
expect(updated).toContain("output: 'standalone'");
|
|
21
|
+
expect(updated).toContain("transpilePackages: ['@sample-app/shared']");
|
|
22
|
+
expect(updated).toContain("serverExternalPackages: ['applicationinsights', 'diagnostic-channel-publishers']");
|
|
23
|
+
expect(updated).not.toContain("turbopackUseSystemTlsCerts");
|
|
24
|
+
expect(updated).not.toContain("experimental:");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("supports JavaScript next.config format", () => {
|
|
28
|
+
const original = `const nextConfig = {
|
|
29
|
+
/* config options here */
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
module.exports = nextConfig;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const updated = injectSwallowKitNextConfig(original, "sample-app");
|
|
36
|
+
|
|
37
|
+
expect(updated).toContain("transpilePackages: ['@sample-app/shared']");
|
|
38
|
+
expect(updated).toContain("module.exports = nextConfig;");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("generates a C# Program.cs compatible with the current worker packages", () => {
|
|
42
|
+
const source = buildCSharpFunctionsProgramSource();
|
|
43
|
+
|
|
44
|
+
expect(source).toContain("new HostBuilder()");
|
|
45
|
+
expect(source).toContain(".ConfigureFunctionsWorkerDefaults()");
|
|
46
|
+
expect(source).toContain("services.AddApplicationInsightsTelemetryWorkerService()");
|
|
47
|
+
expect(source).not.toContain("Microsoft.Azure.Functions.Worker.Builder");
|
|
48
|
+
expect(source).not.toContain("FunctionsApplication.CreateBuilder");
|
|
49
|
+
expect(source).not.toContain("ConfigureFunctionsApplicationInsights");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("excludes nested generated bin and obj files from the C# Functions project", () => {
|
|
53
|
+
const source = buildCSharpFunctionsProjectSource();
|
|
54
|
+
|
|
55
|
+
expect(source).toContain('<Compile Remove="generated\\**\\bin\\**\\*.cs;generated\\**\\obj\\**\\*.cs" />');
|
|
56
|
+
expect(source).toContain('<EmbeddedResource Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />');
|
|
57
|
+
expect(source).toContain('<None Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateBFFCallFunction,
|
|
3
|
+
generateCompactBFFRoutes,
|
|
4
|
+
} from "../core/scaffold/nextjs-generator";
|
|
5
|
+
import { createBasicModelInfo } from "./fixtures";
|
|
6
|
+
|
|
7
|
+
describe("generateBFFCallFunction", () => {
|
|
8
|
+
it("generates call function helper code", () => {
|
|
9
|
+
const code = generateBFFCallFunction();
|
|
10
|
+
expect(code).toMatchSnapshot();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("includes getFunctionsBaseUrl helper", () => {
|
|
14
|
+
const code = generateBFFCallFunction();
|
|
15
|
+
expect(code).toContain("getFunctionsBaseUrl");
|
|
16
|
+
expect(code).toContain("BACKEND_FUNCTIONS_BASE_URL");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("includes callFunction export", () => {
|
|
20
|
+
const code = generateBFFCallFunction();
|
|
21
|
+
expect(code).toContain("export async function callFunction");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("includes z.ZodSchema typings", () => {
|
|
25
|
+
const code = generateBFFCallFunction();
|
|
26
|
+
expect(code).toContain("z.ZodSchema");
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("generateCompactBFFRoutes", () => {
|
|
31
|
+
it("generates list and detail routes", () => {
|
|
32
|
+
const model = createBasicModelInfo();
|
|
33
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
34
|
+
|
|
35
|
+
expect(routes.listRoute).toMatchSnapshot();
|
|
36
|
+
expect(routes.detailRoute).toMatchSnapshot();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("imports the correct schema", () => {
|
|
40
|
+
const model = createBasicModelInfo();
|
|
41
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
42
|
+
|
|
43
|
+
expect(routes.listRoute).toContain(
|
|
44
|
+
"import { todoSchema } from '@myapp/shared'"
|
|
45
|
+
);
|
|
46
|
+
expect(routes.detailRoute).toContain(
|
|
47
|
+
"import { todoSchema } from '@myapp/shared'"
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("uses correct API paths (camelCase)", () => {
|
|
52
|
+
const model = createBasicModelInfo();
|
|
53
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
54
|
+
|
|
55
|
+
expect(routes.listRoute).toContain("path: '/api/todo'");
|
|
56
|
+
expect(routes.detailRoute).toContain("/api/todo/");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("list route has GET and POST handlers", () => {
|
|
60
|
+
const model = createBasicModelInfo();
|
|
61
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
62
|
+
|
|
63
|
+
expect(routes.listRoute).toContain("export async function GET()");
|
|
64
|
+
expect(routes.listRoute).toContain("export async function POST(");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("detail route has GET, PUT, and DELETE handlers", () => {
|
|
68
|
+
const model = createBasicModelInfo();
|
|
69
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
70
|
+
|
|
71
|
+
expect(routes.detailRoute).toContain("export async function GET(");
|
|
72
|
+
expect(routes.detailRoute).toContain("export async function PUT(");
|
|
73
|
+
expect(routes.detailRoute).toContain("export async function DELETE(");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("creates InputSchema that omits managed fields", () => {
|
|
77
|
+
const model = createBasicModelInfo();
|
|
78
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
79
|
+
|
|
80
|
+
expect(routes.listRoute).toContain(
|
|
81
|
+
"todoSchema.omit({ id: true, createdAt: true, updatedAt: true })"
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("handles multi-word model names", () => {
|
|
86
|
+
const model = createBasicModelInfo({
|
|
87
|
+
name: "BlogPost",
|
|
88
|
+
schemaName: "blogPostSchema",
|
|
89
|
+
});
|
|
90
|
+
const routes = generateCompactBFFRoutes(model, "@myapp/shared");
|
|
91
|
+
|
|
92
|
+
expect(routes.listRoute).toContain("path: '/api/blogPost'");
|
|
93
|
+
expect(routes.listRoute).toContain(
|
|
94
|
+
"import { blogPostSchema } from '@myapp/shared'"
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { generateOpenApiDocument } from "../core/scaffold/openapi-generator";
|
|
2
|
+
import { createBasicModelInfo } from "./fixtures";
|
|
3
|
+
|
|
4
|
+
describe("generateOpenApiDocument", () => {
|
|
5
|
+
it("emits an OpenAPI document for the root model", () => {
|
|
6
|
+
const model = createBasicModelInfo();
|
|
7
|
+
const document = JSON.parse(generateOpenApiDocument([model], model));
|
|
8
|
+
|
|
9
|
+
expect(document.openapi).toBe("3.0.3");
|
|
10
|
+
expect(document.components.schemas.Todo.properties.title.type).toBe("string");
|
|
11
|
+
expect(document.paths["/api/todo"].post.requestBody.content["application/json"].schema.$ref)
|
|
12
|
+
.toBe("#/components/schemas/Todo");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("emits nested schema references when present", () => {
|
|
16
|
+
const todo = createBasicModelInfo({
|
|
17
|
+
fields: [
|
|
18
|
+
{ name: "id", type: "string", isOptional: false, isArray: false },
|
|
19
|
+
{ name: "title", type: "string", isOptional: false, isArray: false },
|
|
20
|
+
{
|
|
21
|
+
name: "category",
|
|
22
|
+
type: "object",
|
|
23
|
+
isOptional: false,
|
|
24
|
+
isArray: false,
|
|
25
|
+
isNestedSchema: true,
|
|
26
|
+
nestedModelName: "Category",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const category = createBasicModelInfo({
|
|
31
|
+
name: "Category",
|
|
32
|
+
schemaName: "categorySchema",
|
|
33
|
+
fields: [
|
|
34
|
+
{ name: "id", type: "string", isOptional: false, isArray: false },
|
|
35
|
+
{ name: "name", type: "string", isOptional: false, isArray: false },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const document = JSON.parse(generateOpenApiDocument([todo, category], todo));
|
|
40
|
+
expect(document.components.schemas.Todo.properties.category.$ref).toBe("#/components/schemas/Category");
|
|
41
|
+
expect(document.components.schemas.Category.properties.name.type).toBe("string");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCommands,
|
|
3
|
+
getWorkspaceConfig,
|
|
4
|
+
getCiSetupStep,
|
|
5
|
+
getAzurePipelinesSetup,
|
|
6
|
+
getBuildScript,
|
|
7
|
+
getFunctionsPrestart,
|
|
8
|
+
getFunctionsStartScript,
|
|
9
|
+
spawnArgs,
|
|
10
|
+
} from "../utils/package-manager";
|
|
11
|
+
|
|
12
|
+
describe("getCommands", () => {
|
|
13
|
+
describe("pnpm", () => {
|
|
14
|
+
const cmds = getCommands("pnpm");
|
|
15
|
+
|
|
16
|
+
it("returns correct name", () => {
|
|
17
|
+
expect(cmds.name).toBe("pnpm");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns correct install command", () => {
|
|
21
|
+
expect(cmds.install).toBe("pnpm install");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("returns correct ci command", () => {
|
|
25
|
+
expect(cmds.ci).toBe("pnpm install --frozen-lockfile");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("returns correct add command", () => {
|
|
29
|
+
expect(cmds.add).toBe("pnpm add");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("returns correct addDev command", () => {
|
|
33
|
+
expect(cmds.addDev).toBe("pnpm add -D");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("returns correct exec command", () => {
|
|
37
|
+
expect(cmds.exec).toBe("pnpm exec");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("returns correct dlx command", () => {
|
|
41
|
+
expect(cmds.dlx).toBe("pnpm dlx");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("returns correct runFilter for workspace", () => {
|
|
45
|
+
expect(cmds.runFilter("shared")).toBe("pnpm run --filter shared");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns --use-pnpm flag for create-next-app", () => {
|
|
49
|
+
expect(cmds.createNextAppFlag).toBe("--use-pnpm");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("npm", () => {
|
|
54
|
+
const cmds = getCommands("npm");
|
|
55
|
+
|
|
56
|
+
it("returns correct name", () => {
|
|
57
|
+
expect(cmds.name).toBe("npm");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns correct install command", () => {
|
|
61
|
+
expect(cmds.install).toBe("npm install");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("returns correct ci command", () => {
|
|
65
|
+
expect(cmds.ci).toBe("npm ci");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("returns correct add command", () => {
|
|
69
|
+
expect(cmds.add).toBe("npm install");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("returns correct addDev command", () => {
|
|
73
|
+
expect(cmds.addDev).toBe("npm install -D");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("returns correct exec command", () => {
|
|
77
|
+
expect(cmds.exec).toBe("npx");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("returns correct dlx command", () => {
|
|
81
|
+
expect(cmds.dlx).toBe("npx");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("returns correct runFilter for workspace", () => {
|
|
85
|
+
expect(cmds.runFilter("shared")).toBe("npm run --workspace=shared");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("returns null for create-next-app flag", () => {
|
|
89
|
+
expect(cmds.createNextAppFlag).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("getWorkspaceConfig", () => {
|
|
95
|
+
it("returns pnpm-workspace.yaml config for pnpm", () => {
|
|
96
|
+
const config = getWorkspaceConfig("pnpm", ["packages/*", "apps/*"]);
|
|
97
|
+
expect(config.type).toBe("file");
|
|
98
|
+
if (config.type === "file") {
|
|
99
|
+
expect(config.filename).toBe("pnpm-workspace.yaml");
|
|
100
|
+
expect(config.content).toContain("packages:");
|
|
101
|
+
expect(config.content).toContain(" - packages/*");
|
|
102
|
+
expect(config.content).toContain(" - apps/*");
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns packageJson config for npm", () => {
|
|
107
|
+
const config = getWorkspaceConfig("npm", ["packages/*", "apps/*"]);
|
|
108
|
+
expect(config.type).toBe("packageJson");
|
|
109
|
+
if (config.type === "packageJson") {
|
|
110
|
+
expect(config.field).toBe("workspaces");
|
|
111
|
+
expect(config.value).toEqual(["packages/*", "apps/*"]);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("getCiSetupStep", () => {
|
|
117
|
+
it("returns pnpm setup step for pnpm", () => {
|
|
118
|
+
const step = getCiSetupStep("pnpm");
|
|
119
|
+
expect(step).toContain("pnpm/action-setup");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("returns empty string for npm", () => {
|
|
123
|
+
expect(getCiSetupStep("npm")).toBe("");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("getAzurePipelinesSetup", () => {
|
|
128
|
+
it("returns corepack step for pnpm", () => {
|
|
129
|
+
const step = getAzurePipelinesSetup("pnpm");
|
|
130
|
+
expect(step).toContain("corepack enable");
|
|
131
|
+
expect(step).toContain("pnpm");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("returns empty string for npm", () => {
|
|
135
|
+
expect(getAzurePipelinesSetup("npm")).toBe("");
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("getBuildScript", () => {
|
|
140
|
+
it("uses pnpm run --filter for pnpm", () => {
|
|
141
|
+
expect(getBuildScript("pnpm")).toContain("pnpm run --filter shared build");
|
|
142
|
+
expect(getBuildScript("pnpm")).toContain("fs.cpSync");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("uses npm run --workspace for npm", () => {
|
|
146
|
+
expect(getBuildScript("npm")).toContain("npm run --workspace=shared build");
|
|
147
|
+
expect(getBuildScript("npm")).toContain("fs.cpSync");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("getFunctionsPrestart", () => {
|
|
152
|
+
it("uses pnpm run build for pnpm", () => {
|
|
153
|
+
expect(getFunctionsPrestart("pnpm")).toBe("pnpm run build");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("uses npm run build for npm", () => {
|
|
157
|
+
expect(getFunctionsPrestart("npm")).toBe("npm run build");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("getFunctionsStartScript", () => {
|
|
162
|
+
it("uses pnpm start for pnpm", () => {
|
|
163
|
+
expect(getFunctionsStartScript("pnpm")).toContain("pnpm start");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("uses npm start for npm", () => {
|
|
167
|
+
expect(getFunctionsStartScript("npm")).toContain("npm start");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("uses func start for csharp backends", () => {
|
|
171
|
+
expect(getFunctionsStartScript("pnpm", "csharp")).toBe("cd functions && func start");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("uses func start for python backends", () => {
|
|
175
|
+
expect(getFunctionsStartScript("npm", "python")).toBe("cd functions && func start");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("spawnArgs", () => {
|
|
180
|
+
it("returns cmd and args for pnpm", () => {
|
|
181
|
+
const result = spawnArgs("pnpm", ["add", "next@latest"]);
|
|
182
|
+
expect(result).toEqual({ cmd: "pnpm", args: ["add", "next@latest"] });
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("returns cmd and args for npm", () => {
|
|
186
|
+
const result = spawnArgs("npm", ["install", "next@latest"]);
|
|
187
|
+
expect(result).toEqual({ cmd: "npm", args: ["install", "next@latest"] });
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { getCSharpSchemaArtifactPruneTargets, getOpenApiGeneratorArgs } from "../cli/commands/scaffold";
|
|
3
|
+
|
|
4
|
+
describe("getOpenApiGeneratorArgs", () => {
|
|
5
|
+
it("keeps supporting files for C# model generation", () => {
|
|
6
|
+
const args = getOpenApiGeneratorArgs("spec.json", "out", "csharp");
|
|
7
|
+
const globalPropertyIndex = args.indexOf("--global-property");
|
|
8
|
+
|
|
9
|
+
expect(globalPropertyIndex).toBeGreaterThanOrEqual(0);
|
|
10
|
+
expect(args[globalPropertyIndex + 1]).toBe("models,supportingFiles,apis=false,modelDocs=false,modelTests=false");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("continues omitting supporting files for Python model generation", () => {
|
|
14
|
+
const args = getOpenApiGeneratorArgs("spec.json", "out", "python");
|
|
15
|
+
const globalPropertyIndex = args.indexOf("--global-property");
|
|
16
|
+
|
|
17
|
+
expect(globalPropertyIndex).toBeGreaterThanOrEqual(0);
|
|
18
|
+
expect(args[globalPropertyIndex + 1]).toBe(
|
|
19
|
+
"models,apis=false,supportingFiles=false,modelDocs=false,modelTests=false"
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("prunes extra C# artifacts that pull in unused dependencies", () => {
|
|
24
|
+
const targets = getCSharpSchemaArtifactPruneTargets("C:\\temp\\generated\\csharp-models");
|
|
25
|
+
|
|
26
|
+
expect(targets).toEqual([
|
|
27
|
+
path.join("C:\\temp\\generated\\csharp-models", "src", "SwallowKitBackendModels.Test"),
|
|
28
|
+
path.join("C:\\temp\\generated\\csharp-models", "src", "SwallowKitBackendModels", "Api"),
|
|
29
|
+
path.join("C:\\temp\\generated\\csharp-models", "src", "SwallowKitBackendModels", "Extensions"),
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("keeps only Option.cs from generated C# client helpers", () => {
|
|
34
|
+
const args = getOpenApiGeneratorArgs("spec.json", "out", "csharp");
|
|
35
|
+
|
|
36
|
+
expect(args).toContain("csharp");
|
|
37
|
+
expect(getCSharpSchemaArtifactPruneTargets("C:\\temp\\generated\\csharp-models")).toHaveLength(3);
|
|
38
|
+
});
|
|
39
|
+
});
|