@ingenyus/swarm-wasp 0.1.0 → 0.2.1
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 +229 -21
- package/dist/.tsbuildinfo +1 -1
- package/dist/common/filesystem.d.ts +0 -14
- package/dist/common/filesystem.d.ts.map +1 -1
- package/dist/common/filesystem.js +123 -0
- package/dist/common/index.d.ts +0 -1
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +366 -0
- package/dist/common/prisma.js +140 -0
- package/dist/common/schemas.d.ts +8 -42
- package/dist/common/schemas.d.ts.map +1 -1
- package/dist/common/schemas.js +54 -0
- package/dist/common/templates.js +52 -0
- package/dist/generators/action/action-generator.d.ts +16 -29
- package/dist/generators/action/action-generator.d.ts.map +1 -1
- package/dist/generators/action/action-generator.js +1425 -0
- package/dist/generators/action/index.js +1425 -0
- package/dist/generators/action/schema.d.ts +11 -23
- package/dist/generators/action/schema.d.ts.map +1 -1
- package/dist/generators/action/schema.js +115 -0
- package/dist/generators/api/api-generator.d.ts +19 -26
- package/dist/generators/api/api-generator.d.ts.map +1 -1
- package/dist/generators/api/api-generator.js +1104 -0
- package/dist/generators/api/index.js +1104 -0
- package/dist/generators/api/schema.d.ts +13 -21
- package/dist/generators/api/schema.d.ts.map +1 -1
- package/dist/generators/api/schema.js +117 -0
- package/dist/generators/api-namespace/api-namespace-generator.d.ts +10 -17
- package/dist/generators/api-namespace/api-namespace-generator.d.ts.map +1 -1
- package/dist/generators/api-namespace/api-namespace-generator.js +1028 -0
- package/dist/generators/api-namespace/index.js +1028 -0
- package/dist/generators/api-namespace/schema.d.ts +4 -12
- package/dist/generators/api-namespace/schema.d.ts.map +1 -1
- package/dist/generators/api-namespace/schema.js +89 -0
- package/dist/generators/base/{entity-generator.base.d.ts → component-generator.base.d.ts} +9 -9
- package/dist/generators/base/component-generator.base.d.ts.map +1 -0
- package/dist/generators/base/component-generator.base.js +931 -0
- package/dist/generators/base/index.d.ts +1 -1
- package/dist/generators/base/index.d.ts.map +1 -1
- package/dist/generators/base/index.js +1330 -0
- package/dist/generators/base/operation-generator.base.d.ts +12 -3
- package/dist/generators/base/operation-generator.base.d.ts.map +1 -1
- package/dist/generators/base/operation-generator.base.js +1331 -0
- package/dist/generators/base/wasp-generator.base.d.ts +2 -1
- package/dist/generators/base/wasp-generator.base.d.ts.map +1 -1
- package/dist/generators/base/wasp-generator.base.js +706 -0
- package/dist/generators/config/config-generator.d.ts +7 -4
- package/dist/generators/config/config-generator.d.ts.map +1 -1
- package/dist/generators/config/config-generator.js +0 -0
- package/dist/generators/config/index.js +596 -0
- package/dist/generators/config/wasp-config-generator.d.ts +1 -1
- package/dist/generators/config/wasp-config-generator.d.ts.map +1 -1
- package/dist/generators/config/wasp-config-generator.js +596 -0
- package/dist/generators/crud/crud-generator.d.ts +34 -22
- package/dist/generators/crud/crud-generator.d.ts.map +1 -1
- package/dist/generators/crud/crud-generator.js +1550 -0
- package/dist/generators/crud/index.js +1550 -0
- package/dist/generators/crud/schema.d.ts +25 -18
- package/dist/generators/crud/schema.d.ts.map +1 -1
- package/dist/generators/crud/schema.js +133 -0
- package/dist/generators/feature/feature-generator.d.ts +20 -0
- package/dist/generators/feature/feature-generator.d.ts.map +1 -0
- package/dist/generators/feature/feature-generator.js +765 -0
- package/dist/generators/feature/index.d.ts +2 -0
- package/dist/generators/feature/index.d.ts.map +1 -0
- package/dist/generators/feature/index.js +765 -0
- package/dist/generators/feature/schema.d.ts +5 -0
- package/dist/generators/feature/schema.d.ts.map +1 -0
- package/dist/generators/feature/schema.js +86 -0
- package/dist/generators/index.d.ts +1 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2211 -0
- package/dist/generators/job/index.js +1099 -0
- package/dist/generators/job/job-generator.d.ts +12 -23
- package/dist/generators/job/job-generator.d.ts.map +1 -1
- package/dist/generators/job/job-generator.js +1099 -0
- package/dist/generators/job/schema.d.ts +6 -18
- package/dist/generators/job/schema.d.ts.map +1 -1
- package/dist/generators/job/schema.js +152 -0
- package/dist/generators/query/index.js +1425 -0
- package/dist/generators/query/query-generator.d.ts +16 -29
- package/dist/generators/query/query-generator.d.ts.map +1 -1
- package/dist/generators/query/query-generator.js +1425 -0
- package/dist/generators/query/schema.d.ts +11 -23
- package/dist/generators/query/schema.d.ts.map +1 -1
- package/dist/generators/query/schema.js +115 -0
- package/dist/generators/route/index.js +1038 -0
- package/dist/generators/route/route-generator.d.ts +11 -20
- package/dist/generators/route/route-generator.d.ts.map +1 -1
- package/dist/generators/route/route-generator.js +1038 -0
- package/dist/generators/route/schema.d.ts +5 -15
- package/dist/generators/route/schema.d.ts.map +1 -1
- package/dist/generators/route/schema.js +90 -0
- package/dist/index.d.ts +2 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1980 -2115
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/wasp.d.ts +3 -0
- package/dist/plugins/wasp.d.ts.map +1 -0
- package/dist/types/constants.d.ts +4 -22
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/constants.js +8 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -2
- package/dist/wasp-config/app.d.ts +2 -1
- package/dist/wasp-config/app.d.ts.map +1 -1
- package/dist/wasp-config/app.js +357 -0
- package/dist/wasp-config/index.js +357 -0
- package/dist/wasp-config/stubs/index.js +48 -0
- package/package.json +5 -14
- package/dist/common/plugin.d.ts +0 -2
- package/dist/common/plugin.d.ts.map +0 -1
- package/dist/generators/args.types.d.ts +0 -85
- package/dist/generators/args.types.d.ts.map +0 -1
- package/dist/generators/base/entity-generator.base.d.ts.map +0 -1
- package/dist/generators/feature-directory/feature-directory-generator.d.ts +0 -18
- package/dist/generators/feature-directory/feature-directory-generator.d.ts.map +0 -1
- package/dist/generators/feature-directory/index.d.ts +0 -2
- package/dist/generators/feature-directory/index.d.ts.map +0 -1
- package/dist/generators/feature-directory/schema.d.ts +0 -8
- package/dist/generators/feature-directory/schema.d.ts.map +0 -1
- package/dist/plugin.d.ts +0 -6
- package/dist/plugin.d.ts.map +0 -1
- /package/dist/generators/{feature-directory → feature}/templates/feature.wasp.eta +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// src/common/filesystem.ts
|
|
2
|
+
import { toPascalCase, validateFeaturePath } from "@ingenyus/swarm";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
// src/types/constants.ts
|
|
7
|
+
var TYPE_DIRECTORIES = {
|
|
8
|
+
component: "client/components",
|
|
9
|
+
hook: "client/hooks",
|
|
10
|
+
layout: "client/layouts",
|
|
11
|
+
page: "client/pages",
|
|
12
|
+
util: "client/utils",
|
|
13
|
+
action: "server/actions",
|
|
14
|
+
query: "server/queries",
|
|
15
|
+
middleware: "server/middleware",
|
|
16
|
+
job: "server/jobs",
|
|
17
|
+
api: "server/apis",
|
|
18
|
+
crud: "server/cruds",
|
|
19
|
+
type: "types"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/common/filesystem.ts
|
|
23
|
+
var realFileSystem = {
|
|
24
|
+
readFileSync: fs.readFileSync,
|
|
25
|
+
writeFileSync: fs.writeFileSync,
|
|
26
|
+
existsSync: fs.existsSync,
|
|
27
|
+
copyFileSync: fs.copyFileSync,
|
|
28
|
+
mkdirSync: fs.mkdirSync,
|
|
29
|
+
readdirSync: fs.readdirSync,
|
|
30
|
+
statSync: fs.statSync
|
|
31
|
+
};
|
|
32
|
+
function findWaspRoot(fileSystem, startDir = process.cwd()) {
|
|
33
|
+
const startDirPath = path.resolve(startDir);
|
|
34
|
+
let currentDirPath = startDirPath;
|
|
35
|
+
const root = path.parse(currentDirPath).root;
|
|
36
|
+
while (currentDirPath !== root) {
|
|
37
|
+
const waspRootPath = path.join(currentDirPath, ".wasproot");
|
|
38
|
+
if (fileSystem.existsSync(waspRootPath)) {
|
|
39
|
+
return currentDirPath;
|
|
40
|
+
}
|
|
41
|
+
currentDirPath = path.dirname(currentDirPath);
|
|
42
|
+
}
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Couldn't find Wasp application root from ${startDirPath}. Make sure you are running this command from within a Wasp project directory.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function copyDirectory(fileSystem, src, dest) {
|
|
48
|
+
if (!fileSystem.existsSync(dest)) {
|
|
49
|
+
fileSystem.mkdirSync(dest, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
const entries = fileSystem.readdirSync(src, { withFileTypes: true });
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const srcPath = path.join(src, entry.name);
|
|
54
|
+
const destPath = path.join(dest, entry.name);
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
copyDirectory(fileSystem, srcPath, destPath);
|
|
57
|
+
} else {
|
|
58
|
+
fileSystem.copyFileSync(srcPath, destPath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function ensureDirectoryExists(fileSystem, dir) {
|
|
63
|
+
if (!fileSystem.existsSync(dir)) {
|
|
64
|
+
fileSystem.mkdirSync(dir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function featureExists(fileSystem, featurePath) {
|
|
68
|
+
return fileSystem.existsSync(getFeatureDir(fileSystem, featurePath));
|
|
69
|
+
}
|
|
70
|
+
function getConfigDir(fileSystem) {
|
|
71
|
+
const waspRoot = findWaspRoot(fileSystem);
|
|
72
|
+
return path.join(waspRoot, "config");
|
|
73
|
+
}
|
|
74
|
+
function normaliseFeaturePath(featurePath) {
|
|
75
|
+
const segments = validateFeaturePath(featurePath);
|
|
76
|
+
const normalisedSegments = [];
|
|
77
|
+
for (let i = 0; i < segments.length; i++) {
|
|
78
|
+
const segment = segments[i];
|
|
79
|
+
const previousSegment = normalisedSegments[normalisedSegments.length - 1];
|
|
80
|
+
if (previousSegment !== "features" && segment !== "features") {
|
|
81
|
+
normalisedSegments.push("features");
|
|
82
|
+
}
|
|
83
|
+
normalisedSegments.push(segment);
|
|
84
|
+
}
|
|
85
|
+
return normalisedSegments.join("/");
|
|
86
|
+
}
|
|
87
|
+
function getFeatureDir(fileSystem, featureName) {
|
|
88
|
+
const waspRoot = findWaspRoot(fileSystem);
|
|
89
|
+
const normalisedPath = normaliseFeaturePath(featureName);
|
|
90
|
+
return path.join(waspRoot, "src", normalisedPath);
|
|
91
|
+
}
|
|
92
|
+
function getFeatureImportPath(featurePath) {
|
|
93
|
+
const segments = validateFeaturePath(featurePath);
|
|
94
|
+
return segments.join("/");
|
|
95
|
+
}
|
|
96
|
+
function getFeatureTargetDir(fileSystem, featurePath, type) {
|
|
97
|
+
validateFeaturePath(featurePath);
|
|
98
|
+
const normalisedPath = normaliseFeaturePath(featurePath);
|
|
99
|
+
const featureDir = getFeatureDir(fileSystem, normalisedPath);
|
|
100
|
+
const typeKey = type.toLowerCase();
|
|
101
|
+
const typeDirectory = TYPE_DIRECTORIES[typeKey];
|
|
102
|
+
const targetDirectory = path.join(featureDir, typeDirectory);
|
|
103
|
+
const importDirectory = `@src/${normalisedPath}/${typeDirectory}`;
|
|
104
|
+
return { targetDirectory, importDirectory };
|
|
105
|
+
}
|
|
106
|
+
function getRouteNameFromPath(routePath) {
|
|
107
|
+
const lastSegment = routePath.split("/").filter(Boolean).pop() || "index";
|
|
108
|
+
const cleanSegment = lastSegment.replace(/[:*]/g, "");
|
|
109
|
+
return `${toPascalCase(cleanSegment)}Page`;
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
copyDirectory,
|
|
113
|
+
ensureDirectoryExists,
|
|
114
|
+
featureExists,
|
|
115
|
+
findWaspRoot,
|
|
116
|
+
getConfigDir,
|
|
117
|
+
getFeatureDir,
|
|
118
|
+
getFeatureImportPath,
|
|
119
|
+
getFeatureTargetDir,
|
|
120
|
+
getRouteNameFromPath,
|
|
121
|
+
normaliseFeaturePath,
|
|
122
|
+
realFileSystem
|
|
123
|
+
};
|
package/dist/common/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
// src/common/filesystem.ts
|
|
2
|
+
import { toPascalCase, validateFeaturePath } from "@ingenyus/swarm";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
// src/types/constants.ts
|
|
7
|
+
var TYPE_DIRECTORIES = {
|
|
8
|
+
component: "client/components",
|
|
9
|
+
hook: "client/hooks",
|
|
10
|
+
layout: "client/layouts",
|
|
11
|
+
page: "client/pages",
|
|
12
|
+
util: "client/utils",
|
|
13
|
+
action: "server/actions",
|
|
14
|
+
query: "server/queries",
|
|
15
|
+
middleware: "server/middleware",
|
|
16
|
+
job: "server/jobs",
|
|
17
|
+
api: "server/apis",
|
|
18
|
+
crud: "server/cruds",
|
|
19
|
+
type: "types"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/common/filesystem.ts
|
|
23
|
+
var realFileSystem = {
|
|
24
|
+
readFileSync: fs.readFileSync,
|
|
25
|
+
writeFileSync: fs.writeFileSync,
|
|
26
|
+
existsSync: fs.existsSync,
|
|
27
|
+
copyFileSync: fs.copyFileSync,
|
|
28
|
+
mkdirSync: fs.mkdirSync,
|
|
29
|
+
readdirSync: fs.readdirSync,
|
|
30
|
+
statSync: fs.statSync
|
|
31
|
+
};
|
|
32
|
+
function findWaspRoot(fileSystem, startDir = process.cwd()) {
|
|
33
|
+
const startDirPath = path.resolve(startDir);
|
|
34
|
+
let currentDirPath = startDirPath;
|
|
35
|
+
const root = path.parse(currentDirPath).root;
|
|
36
|
+
while (currentDirPath !== root) {
|
|
37
|
+
const waspRootPath = path.join(currentDirPath, ".wasproot");
|
|
38
|
+
if (fileSystem.existsSync(waspRootPath)) {
|
|
39
|
+
return currentDirPath;
|
|
40
|
+
}
|
|
41
|
+
currentDirPath = path.dirname(currentDirPath);
|
|
42
|
+
}
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Couldn't find Wasp application root from ${startDirPath}. Make sure you are running this command from within a Wasp project directory.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function copyDirectory(fileSystem, src, dest) {
|
|
48
|
+
if (!fileSystem.existsSync(dest)) {
|
|
49
|
+
fileSystem.mkdirSync(dest, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
const entries = fileSystem.readdirSync(src, { withFileTypes: true });
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const srcPath = path.join(src, entry.name);
|
|
54
|
+
const destPath = path.join(dest, entry.name);
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
copyDirectory(fileSystem, srcPath, destPath);
|
|
57
|
+
} else {
|
|
58
|
+
fileSystem.copyFileSync(srcPath, destPath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function ensureDirectoryExists(fileSystem, dir) {
|
|
63
|
+
if (!fileSystem.existsSync(dir)) {
|
|
64
|
+
fileSystem.mkdirSync(dir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function featureExists(fileSystem, featurePath) {
|
|
68
|
+
return fileSystem.existsSync(getFeatureDir(fileSystem, featurePath));
|
|
69
|
+
}
|
|
70
|
+
function getConfigDir(fileSystem) {
|
|
71
|
+
const waspRoot = findWaspRoot(fileSystem);
|
|
72
|
+
return path.join(waspRoot, "config");
|
|
73
|
+
}
|
|
74
|
+
function normaliseFeaturePath(featurePath) {
|
|
75
|
+
const segments = validateFeaturePath(featurePath);
|
|
76
|
+
const normalisedSegments = [];
|
|
77
|
+
for (let i = 0; i < segments.length; i++) {
|
|
78
|
+
const segment = segments[i];
|
|
79
|
+
const previousSegment = normalisedSegments[normalisedSegments.length - 1];
|
|
80
|
+
if (previousSegment !== "features" && segment !== "features") {
|
|
81
|
+
normalisedSegments.push("features");
|
|
82
|
+
}
|
|
83
|
+
normalisedSegments.push(segment);
|
|
84
|
+
}
|
|
85
|
+
return normalisedSegments.join("/");
|
|
86
|
+
}
|
|
87
|
+
function getFeatureDir(fileSystem, featureName) {
|
|
88
|
+
const waspRoot = findWaspRoot(fileSystem);
|
|
89
|
+
const normalisedPath = normaliseFeaturePath(featureName);
|
|
90
|
+
return path.join(waspRoot, "src", normalisedPath);
|
|
91
|
+
}
|
|
92
|
+
function getFeatureImportPath(featurePath) {
|
|
93
|
+
const segments = validateFeaturePath(featurePath);
|
|
94
|
+
return segments.join("/");
|
|
95
|
+
}
|
|
96
|
+
function getFeatureTargetDir(fileSystem, featurePath, type) {
|
|
97
|
+
validateFeaturePath(featurePath);
|
|
98
|
+
const normalisedPath = normaliseFeaturePath(featurePath);
|
|
99
|
+
const featureDir = getFeatureDir(fileSystem, normalisedPath);
|
|
100
|
+
const typeKey = type.toLowerCase();
|
|
101
|
+
const typeDirectory = TYPE_DIRECTORIES[typeKey];
|
|
102
|
+
const targetDirectory = path.join(featureDir, typeDirectory);
|
|
103
|
+
const importDirectory = `@src/${normalisedPath}/${typeDirectory}`;
|
|
104
|
+
return { targetDirectory, importDirectory };
|
|
105
|
+
}
|
|
106
|
+
function getRouteNameFromPath(routePath) {
|
|
107
|
+
const lastSegment = routePath.split("/").filter(Boolean).pop() || "index";
|
|
108
|
+
const cleanSegment = lastSegment.replace(/[:*]/g, "");
|
|
109
|
+
return `${toPascalCase(cleanSegment)}Page`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/common/prisma.ts
|
|
113
|
+
import {
|
|
114
|
+
getSchema
|
|
115
|
+
} from "@mrleebo/prisma-ast";
|
|
116
|
+
import fs2 from "fs";
|
|
117
|
+
import path2 from "path";
|
|
118
|
+
async function getEntityMetadata(modelName) {
|
|
119
|
+
try {
|
|
120
|
+
const schemaPath = path2.join(process.cwd(), "schema.prisma");
|
|
121
|
+
const schemaContent = fs2.readFileSync(schemaPath, "utf8");
|
|
122
|
+
const schema = getSchema(schemaContent);
|
|
123
|
+
const model = schema.list?.find(
|
|
124
|
+
(m) => m.type === "model" && m.name === modelName
|
|
125
|
+
);
|
|
126
|
+
if (!model || model.type !== "model") {
|
|
127
|
+
throw new Error(`Model ${modelName} not found in schema`);
|
|
128
|
+
}
|
|
129
|
+
const compositeIdAttr = (model.properties || []).find(
|
|
130
|
+
(item) => item.type === "attribute" && item.kind === "object" && item.name === "id"
|
|
131
|
+
);
|
|
132
|
+
let compositeIdFields = [];
|
|
133
|
+
if (compositeIdAttr?.args?.[0]) {
|
|
134
|
+
const arg = compositeIdAttr.args[0];
|
|
135
|
+
if (typeof arg.value === "object" && arg.value !== null && "type" in arg.value && arg.value.type === "array" && "args" in arg.value) {
|
|
136
|
+
compositeIdFields = arg.value.args;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const fields = (model.properties || []).filter(
|
|
140
|
+
(item) => item.type === "field" && !item.array && !item.attributes?.some((attr) => attr.name === "relation")
|
|
141
|
+
).map((field) => {
|
|
142
|
+
const fieldType = typeof field.fieldType === "string" ? field.fieldType : field.fieldType.name;
|
|
143
|
+
const tsType = getPrismaToTsType(fieldType);
|
|
144
|
+
const isRequired = !field.optional;
|
|
145
|
+
const isId = field.attributes?.some((attr) => attr.name === "id") || compositeIdFields.includes(field.name);
|
|
146
|
+
const isUnique = field.attributes?.some((attr) => attr.name === "unique") || false;
|
|
147
|
+
const hasDefaultValue = field.attributes?.some((attr) => attr.name === "default") || false;
|
|
148
|
+
const isUpdatedAt = field.attributes?.some((attr) => attr.name === "updatedAt") || false;
|
|
149
|
+
const isGenerated = field.attributes?.some((attr) => attr.name === "map") || false;
|
|
150
|
+
return {
|
|
151
|
+
name: field.name,
|
|
152
|
+
type: fieldType,
|
|
153
|
+
tsType,
|
|
154
|
+
isRequired,
|
|
155
|
+
isId,
|
|
156
|
+
isUnique,
|
|
157
|
+
hasDefaultValue,
|
|
158
|
+
isGenerated,
|
|
159
|
+
isUpdatedAt
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
name: modelName,
|
|
164
|
+
fields
|
|
165
|
+
};
|
|
166
|
+
} catch (error) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Failed to get entity metadata for ${modelName}: ${error instanceof Error ? error.message : String(error)}`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function getIdFields(model) {
|
|
173
|
+
const idFields = model.fields.filter((f) => f.isId).map((f) => f.name);
|
|
174
|
+
if (idFields.length === 0) {
|
|
175
|
+
throw new Error(`No ID field found for model ${model.name}`);
|
|
176
|
+
}
|
|
177
|
+
return idFields;
|
|
178
|
+
}
|
|
179
|
+
function getRequiredFields(model) {
|
|
180
|
+
return model.fields.filter(
|
|
181
|
+
(f) => f.isRequired && !f.hasDefaultValue && !f.isGenerated && !f.isUpdatedAt
|
|
182
|
+
).map((f) => f.name);
|
|
183
|
+
}
|
|
184
|
+
function getOptionalFields(model) {
|
|
185
|
+
return model.fields.filter(
|
|
186
|
+
(field) => (field.hasDefaultValue && field.type !== "DateTime" || !field.isRequired) && !field.isId && !field.isGenerated && !field.isUpdatedAt
|
|
187
|
+
).map((field) => field.name);
|
|
188
|
+
}
|
|
189
|
+
function getJsonFields(model) {
|
|
190
|
+
return model.fields.filter((f) => f.type === "Json").map((f) => f.name);
|
|
191
|
+
}
|
|
192
|
+
function generateJsonTypeHandling(jsonFields) {
|
|
193
|
+
if (jsonFields.length === 0) return "";
|
|
194
|
+
const assignments = jsonFields.map(
|
|
195
|
+
(field) => ` ${field}: (data.${field} as Prisma.JsonValue) || Prisma.JsonNull`
|
|
196
|
+
).join(",\n");
|
|
197
|
+
return `,
|
|
198
|
+
${assignments}`;
|
|
199
|
+
}
|
|
200
|
+
function needsPrismaImport(model) {
|
|
201
|
+
return model.fields.some((f) => f.type === "Json" || f.type === "Decimal");
|
|
202
|
+
}
|
|
203
|
+
function generatePickType(modelName, fields, allFields) {
|
|
204
|
+
if (fields.length === 0) return "";
|
|
205
|
+
if (fields.length === allFields.length) return modelName;
|
|
206
|
+
const fieldUnion = fields.map((f) => `"${f}"`).join(" | ");
|
|
207
|
+
return `Pick<${modelName}, ${fieldUnion}>`;
|
|
208
|
+
}
|
|
209
|
+
function generateOmitType(modelName, fields, allFields) {
|
|
210
|
+
if (fields.length === 0) return modelName;
|
|
211
|
+
if (fields.length === allFields.length) return "";
|
|
212
|
+
const fieldUnion = fields.map((f) => `"${f}"`).join(" | ");
|
|
213
|
+
return `Omit<${modelName}, ${fieldUnion}>`;
|
|
214
|
+
}
|
|
215
|
+
function generatePartialType(typeString) {
|
|
216
|
+
if (!typeString) return "";
|
|
217
|
+
return `Partial<${typeString}>`;
|
|
218
|
+
}
|
|
219
|
+
function generateIntersectionType(type1, type2) {
|
|
220
|
+
if (!type1 && !type2) return "";
|
|
221
|
+
if (!type1) return type2;
|
|
222
|
+
if (!type2) return type1;
|
|
223
|
+
return `${type1} & ${type2}`;
|
|
224
|
+
}
|
|
225
|
+
function getPrismaToTsType(type) {
|
|
226
|
+
const typeMap = {
|
|
227
|
+
String: "string",
|
|
228
|
+
Int: "number",
|
|
229
|
+
Float: "number",
|
|
230
|
+
Boolean: "boolean",
|
|
231
|
+
DateTime: "Date",
|
|
232
|
+
Json: "Prisma.JsonValue",
|
|
233
|
+
BigInt: "bigint",
|
|
234
|
+
Decimal: "Prisma.Decimal",
|
|
235
|
+
Bytes: "Buffer"
|
|
236
|
+
};
|
|
237
|
+
return typeMap[type] || type;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/common/schemas.ts
|
|
241
|
+
import { commandRegistry } from "@ingenyus/swarm";
|
|
242
|
+
import { z } from "zod";
|
|
243
|
+
var commonSchemas = {
|
|
244
|
+
feature: z.string().min(1, "Feature is required").meta({
|
|
245
|
+
description: "The feature directory this component will be generated in"
|
|
246
|
+
}).register(commandRegistry, {
|
|
247
|
+
shortName: "f",
|
|
248
|
+
examples: ["root", "auth", "dashboard/users"],
|
|
249
|
+
helpText: "Can be nested as a logical or relative path, e.g. 'dashboard/users' or 'features/dashboard/features/users'"
|
|
250
|
+
}),
|
|
251
|
+
name: z.string().min(1, "Name is required").meta({ description: "The name of the generated component" }).register(commandRegistry, {
|
|
252
|
+
shortName: "n",
|
|
253
|
+
examples: ["users", "task"],
|
|
254
|
+
helpText: "Will be used for generated files and configuration entries"
|
|
255
|
+
}),
|
|
256
|
+
target: z.string().min(1, "Target directory is required").meta({ description: "The target path of the generated directory" }).register(commandRegistry, {
|
|
257
|
+
shortName: "t",
|
|
258
|
+
examples: ["dashboard/users", "features/dashboard/features/users"],
|
|
259
|
+
helpText: "A logical or relative path, e.g. 'dashboard/users' or 'features/dashboard/features/users'"
|
|
260
|
+
}),
|
|
261
|
+
path: z.string().min(1, "Path is required").meta({ description: "The path that this component will be accessible at" }).register(commandRegistry, {
|
|
262
|
+
shortName: "p",
|
|
263
|
+
examples: ["/api/users/:id", "/api/products"],
|
|
264
|
+
helpText: "Supports Express-style placeholders, e.g. '/api/users/:id'"
|
|
265
|
+
}),
|
|
266
|
+
dataType: z.string().min(1, "Data type is required").meta({ description: "The data type/model name for this operation" }).register(commandRegistry, {
|
|
267
|
+
shortName: "d",
|
|
268
|
+
examples: ["User", "Product", "Task"],
|
|
269
|
+
helpText: "The Wasp entity or model name this operation will interact with"
|
|
270
|
+
}),
|
|
271
|
+
entities: z.array(z.string()).optional().meta({
|
|
272
|
+
description: "The Wasp entities that will be available to this component (optional)"
|
|
273
|
+
}).register(commandRegistry, {
|
|
274
|
+
shortName: "e",
|
|
275
|
+
examples: ["User", "User Task"],
|
|
276
|
+
helpText: "An array of Wasp entity names"
|
|
277
|
+
}),
|
|
278
|
+
force: z.boolean().optional().meta({
|
|
279
|
+
description: "Force overwrite of existing files and configuration entries (optional)"
|
|
280
|
+
}).register(commandRegistry, {
|
|
281
|
+
shortName: "F",
|
|
282
|
+
helpText: "CAUTION: Will overwrite existing files and configuration entries with current parameters"
|
|
283
|
+
}),
|
|
284
|
+
auth: z.boolean().optional().meta({
|
|
285
|
+
description: "Require authentication for this component (optional)"
|
|
286
|
+
}).register(commandRegistry, {
|
|
287
|
+
shortName: "a",
|
|
288
|
+
helpText: "Will generate authentication checks"
|
|
289
|
+
})
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/common/templates.ts
|
|
293
|
+
import { toKebabCase } from "@ingenyus/swarm";
|
|
294
|
+
import { Eta } from "eta";
|
|
295
|
+
import path3 from "path";
|
|
296
|
+
var TemplateUtility = class {
|
|
297
|
+
constructor(fileSystem) {
|
|
298
|
+
this.fileSystem = fileSystem;
|
|
299
|
+
}
|
|
300
|
+
processTemplate(templatePath, replacements) {
|
|
301
|
+
const declarations = Object.keys(replacements).map((key) => `${key}=it.${key}`).join(", ");
|
|
302
|
+
const functionHeader = declarations ? `const ${declarations};` : void 0;
|
|
303
|
+
const templateDir = path3.dirname(templatePath);
|
|
304
|
+
const eta = new Eta({
|
|
305
|
+
autoTrim: false,
|
|
306
|
+
autoEscape: false,
|
|
307
|
+
views: templateDir,
|
|
308
|
+
functionHeader
|
|
309
|
+
});
|
|
310
|
+
const templateName = path3.basename(templatePath).replace(/\.eta$/, "");
|
|
311
|
+
if (this.fileSystem.existsSync(templatePath)) {
|
|
312
|
+
return eta.render(templateName, replacements);
|
|
313
|
+
} else {
|
|
314
|
+
const template = this.fileSystem.readFileSync(templatePath, "utf8");
|
|
315
|
+
return eta.renderString(template, replacements);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Helper method to resolve template paths for concrete generators
|
|
320
|
+
* @param relativePath - The relative path to the template file
|
|
321
|
+
* @param generatorName - The name of the generator (e.g., 'api', 'job')
|
|
322
|
+
* @param currentFileUrl - The import.meta.url from the concrete generator class
|
|
323
|
+
* @returns The full path to the template file
|
|
324
|
+
*/
|
|
325
|
+
resolveTemplatePath(relativePath, generatorName, currentFileUrl) {
|
|
326
|
+
const generatorDirName = toKebabCase(generatorName);
|
|
327
|
+
const currentFilePath = new URL(currentFileUrl).pathname;
|
|
328
|
+
const currentFileDir = path3.dirname(currentFilePath);
|
|
329
|
+
const currentFileName = path3.basename(currentFilePath);
|
|
330
|
+
const isInstalledPackage = currentFileDir.includes("node_modules") && currentFileDir.endsWith("/dist") && currentFileName === "index.js";
|
|
331
|
+
const startDir = isInstalledPackage ? currentFileDir : path3.dirname(path3.dirname(currentFileDir));
|
|
332
|
+
return path3.join(
|
|
333
|
+
startDir,
|
|
334
|
+
"generators",
|
|
335
|
+
generatorDirName,
|
|
336
|
+
"templates",
|
|
337
|
+
relativePath
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
export {
|
|
342
|
+
TemplateUtility,
|
|
343
|
+
commonSchemas,
|
|
344
|
+
copyDirectory,
|
|
345
|
+
ensureDirectoryExists,
|
|
346
|
+
featureExists,
|
|
347
|
+
findWaspRoot,
|
|
348
|
+
generateIntersectionType,
|
|
349
|
+
generateJsonTypeHandling,
|
|
350
|
+
generateOmitType,
|
|
351
|
+
generatePartialType,
|
|
352
|
+
generatePickType,
|
|
353
|
+
getConfigDir,
|
|
354
|
+
getEntityMetadata,
|
|
355
|
+
getFeatureDir,
|
|
356
|
+
getFeatureImportPath,
|
|
357
|
+
getFeatureTargetDir,
|
|
358
|
+
getIdFields,
|
|
359
|
+
getJsonFields,
|
|
360
|
+
getOptionalFields,
|
|
361
|
+
getRequiredFields,
|
|
362
|
+
getRouteNameFromPath,
|
|
363
|
+
needsPrismaImport,
|
|
364
|
+
normaliseFeaturePath,
|
|
365
|
+
realFileSystem
|
|
366
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/common/prisma.ts
|
|
2
|
+
import {
|
|
3
|
+
getSchema
|
|
4
|
+
} from "@mrleebo/prisma-ast";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
async function getEntityMetadata(modelName) {
|
|
8
|
+
try {
|
|
9
|
+
const schemaPath = path.join(process.cwd(), "schema.prisma");
|
|
10
|
+
const schemaContent = fs.readFileSync(schemaPath, "utf8");
|
|
11
|
+
const schema = getSchema(schemaContent);
|
|
12
|
+
const model = schema.list?.find(
|
|
13
|
+
(m) => m.type === "model" && m.name === modelName
|
|
14
|
+
);
|
|
15
|
+
if (!model || model.type !== "model") {
|
|
16
|
+
throw new Error(`Model ${modelName} not found in schema`);
|
|
17
|
+
}
|
|
18
|
+
const compositeIdAttr = (model.properties || []).find(
|
|
19
|
+
(item) => item.type === "attribute" && item.kind === "object" && item.name === "id"
|
|
20
|
+
);
|
|
21
|
+
let compositeIdFields = [];
|
|
22
|
+
if (compositeIdAttr?.args?.[0]) {
|
|
23
|
+
const arg = compositeIdAttr.args[0];
|
|
24
|
+
if (typeof arg.value === "object" && arg.value !== null && "type" in arg.value && arg.value.type === "array" && "args" in arg.value) {
|
|
25
|
+
compositeIdFields = arg.value.args;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const fields = (model.properties || []).filter(
|
|
29
|
+
(item) => item.type === "field" && !item.array && !item.attributes?.some((attr) => attr.name === "relation")
|
|
30
|
+
).map((field) => {
|
|
31
|
+
const fieldType = typeof field.fieldType === "string" ? field.fieldType : field.fieldType.name;
|
|
32
|
+
const tsType = getPrismaToTsType(fieldType);
|
|
33
|
+
const isRequired = !field.optional;
|
|
34
|
+
const isId = field.attributes?.some((attr) => attr.name === "id") || compositeIdFields.includes(field.name);
|
|
35
|
+
const isUnique = field.attributes?.some((attr) => attr.name === "unique") || false;
|
|
36
|
+
const hasDefaultValue = field.attributes?.some((attr) => attr.name === "default") || false;
|
|
37
|
+
const isUpdatedAt = field.attributes?.some((attr) => attr.name === "updatedAt") || false;
|
|
38
|
+
const isGenerated = field.attributes?.some((attr) => attr.name === "map") || false;
|
|
39
|
+
return {
|
|
40
|
+
name: field.name,
|
|
41
|
+
type: fieldType,
|
|
42
|
+
tsType,
|
|
43
|
+
isRequired,
|
|
44
|
+
isId,
|
|
45
|
+
isUnique,
|
|
46
|
+
hasDefaultValue,
|
|
47
|
+
isGenerated,
|
|
48
|
+
isUpdatedAt
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
name: modelName,
|
|
53
|
+
fields
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Failed to get entity metadata for ${modelName}: ${error instanceof Error ? error.message : String(error)}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function getIdFields(model) {
|
|
62
|
+
const idFields = model.fields.filter((f) => f.isId).map((f) => f.name);
|
|
63
|
+
if (idFields.length === 0) {
|
|
64
|
+
throw new Error(`No ID field found for model ${model.name}`);
|
|
65
|
+
}
|
|
66
|
+
return idFields;
|
|
67
|
+
}
|
|
68
|
+
function getRequiredFields(model) {
|
|
69
|
+
return model.fields.filter(
|
|
70
|
+
(f) => f.isRequired && !f.hasDefaultValue && !f.isGenerated && !f.isUpdatedAt
|
|
71
|
+
).map((f) => f.name);
|
|
72
|
+
}
|
|
73
|
+
function getOptionalFields(model) {
|
|
74
|
+
return model.fields.filter(
|
|
75
|
+
(field) => (field.hasDefaultValue && field.type !== "DateTime" || !field.isRequired) && !field.isId && !field.isGenerated && !field.isUpdatedAt
|
|
76
|
+
).map((field) => field.name);
|
|
77
|
+
}
|
|
78
|
+
function getJsonFields(model) {
|
|
79
|
+
return model.fields.filter((f) => f.type === "Json").map((f) => f.name);
|
|
80
|
+
}
|
|
81
|
+
function generateJsonTypeHandling(jsonFields) {
|
|
82
|
+
if (jsonFields.length === 0) return "";
|
|
83
|
+
const assignments = jsonFields.map(
|
|
84
|
+
(field) => ` ${field}: (data.${field} as Prisma.JsonValue) || Prisma.JsonNull`
|
|
85
|
+
).join(",\n");
|
|
86
|
+
return `,
|
|
87
|
+
${assignments}`;
|
|
88
|
+
}
|
|
89
|
+
function needsPrismaImport(model) {
|
|
90
|
+
return model.fields.some((f) => f.type === "Json" || f.type === "Decimal");
|
|
91
|
+
}
|
|
92
|
+
function generatePickType(modelName, fields, allFields) {
|
|
93
|
+
if (fields.length === 0) return "";
|
|
94
|
+
if (fields.length === allFields.length) return modelName;
|
|
95
|
+
const fieldUnion = fields.map((f) => `"${f}"`).join(" | ");
|
|
96
|
+
return `Pick<${modelName}, ${fieldUnion}>`;
|
|
97
|
+
}
|
|
98
|
+
function generateOmitType(modelName, fields, allFields) {
|
|
99
|
+
if (fields.length === 0) return modelName;
|
|
100
|
+
if (fields.length === allFields.length) return "";
|
|
101
|
+
const fieldUnion = fields.map((f) => `"${f}"`).join(" | ");
|
|
102
|
+
return `Omit<${modelName}, ${fieldUnion}>`;
|
|
103
|
+
}
|
|
104
|
+
function generatePartialType(typeString) {
|
|
105
|
+
if (!typeString) return "";
|
|
106
|
+
return `Partial<${typeString}>`;
|
|
107
|
+
}
|
|
108
|
+
function generateIntersectionType(type1, type2) {
|
|
109
|
+
if (!type1 && !type2) return "";
|
|
110
|
+
if (!type1) return type2;
|
|
111
|
+
if (!type2) return type1;
|
|
112
|
+
return `${type1} & ${type2}`;
|
|
113
|
+
}
|
|
114
|
+
function getPrismaToTsType(type) {
|
|
115
|
+
const typeMap = {
|
|
116
|
+
String: "string",
|
|
117
|
+
Int: "number",
|
|
118
|
+
Float: "number",
|
|
119
|
+
Boolean: "boolean",
|
|
120
|
+
DateTime: "Date",
|
|
121
|
+
Json: "Prisma.JsonValue",
|
|
122
|
+
BigInt: "bigint",
|
|
123
|
+
Decimal: "Prisma.Decimal",
|
|
124
|
+
Bytes: "Buffer"
|
|
125
|
+
};
|
|
126
|
+
return typeMap[type] || type;
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
generateIntersectionType,
|
|
130
|
+
generateJsonTypeHandling,
|
|
131
|
+
generateOmitType,
|
|
132
|
+
generatePartialType,
|
|
133
|
+
generatePickType,
|
|
134
|
+
getEntityMetadata,
|
|
135
|
+
getIdFields,
|
|
136
|
+
getJsonFields,
|
|
137
|
+
getOptionalFields,
|
|
138
|
+
getRequiredFields,
|
|
139
|
+
needsPrismaImport
|
|
140
|
+
};
|