@cedarjs/cli 5.0.7-next.338 → 5.0.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/README.md +10 -7
- package/dist/cfw.js +1 -3
- package/dist/commands/build/buildHandler.js +6 -14
- package/dist/commands/console.js +3 -5
- package/dist/commands/consoleHandler.js +75 -0
- package/dist/commands/dev/devHandler.js +4 -71
- package/dist/commands/dev.js +4 -7
- package/dist/commands/execHandler.js +0 -12
- package/dist/commands/experimental/setupOpentelemetryHandler.js +1 -1
- package/dist/commands/generate/helpers.js +0 -16
- package/dist/commands/generate/job/jobHandler.js +7 -5
- package/dist/commands/generate/package/filesTask.js +0 -12
- package/dist/commands/generate/package/packageHandler.js +11 -18
- package/dist/commands/generate/package/templates/test.ts.template +2 -2
- package/dist/commands/generate/scaffold/scaffoldHandler.js +19 -138
- package/dist/commands/generate/script/templates/script.ts.template +4 -9
- package/dist/commands/generate/sdl/sdlHandler.js +44 -113
- package/dist/commands/generate/sdl/templates/sdl.js.template +1 -2
- package/dist/commands/generate/sdl/templates/sdl.ts.template +1 -2
- package/dist/commands/generate/service/serviceHandler.js +6 -17
- package/dist/commands/generate/yargsHandlerHelpers.js +6 -0
- package/dist/commands/lint.js +61 -1
- package/dist/commands/prismaHandler.js +7 -4
- package/dist/commands/serve.js +24 -23
- package/dist/commands/serveBothHandler.js +4 -4
- package/dist/commands/setup/auth/auth.js +1 -1
- package/dist/commands/setup/deploy/helpers/index.js +39 -9
- package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +5 -17
- package/dist/commands/setup/deploy/providers/renderHandler.js +21 -27
- package/dist/commands/setup/deploy/templates/render.js +16 -29
- package/dist/commands/setup/docker/templates/Dockerfile.yarn +5 -2
- package/dist/commands/setup/docker/templates/docker-compose.dev.yml +1 -1
- package/dist/commands/setup/docker/templates/docker-compose.prod.yml +5 -2
- package/dist/commands/setup/graphql/features/fragments/appGqlConfigTransform.js +3 -6
- package/dist/commands/setup/monitoring/sentry/sentryHandler.js +4 -9
- package/dist/commands/setup/neon/neon.js +3 -13
- package/dist/commands/setup/neon/neonHandler.js +258 -141
- package/dist/commands/setup/ui/libraries/chakra-uiHandler.js +1 -3
- package/dist/commands/setup/ui/libraries/mantineHandler.js +1 -3
- package/dist/commands/setup/ui/libraries/tailwindcssHandler.js +1 -0
- package/dist/commands/setup/uploads/uploadsHandler.js +5 -6
- package/dist/commands/setup.js +1 -2
- package/dist/commands/test/testHandlerEsm.js +1 -1
- package/dist/commands/upgrade/preUpgradeScripts.js +9 -9
- package/dist/commands/upgrade/upgradeHandler.js +32 -42
- package/dist/lib/background.js +1 -20
- package/dist/lib/exec.js +8 -5
- package/dist/lib/extendFile.js +1 -1
- package/dist/lib/index.js +6 -22
- package/dist/lib/updateCheck.js +6 -34
- package/dist/telemetry/resource.js +8 -3
- package/package.json +27 -26
- package/dist/commands/generate/package/templates/vitest.config.ts.template +0 -9
- package/dist/commands/generate/sdl/stubFiles.js +0 -132
- package/dist/commands/setup/database/database.js +0 -15
- package/dist/commands/setup/database/postgres.js +0 -19
- package/dist/commands/setup/database/postgresHandler.js +0 -194
- /package/dist/commands/setup/{database → neon}/templates/db.ts.template +0 -0
|
@@ -6,7 +6,6 @@ import humanize from "humanize-string";
|
|
|
6
6
|
import { Listr } from "listr2";
|
|
7
7
|
import pascalcase from "pascalcase";
|
|
8
8
|
import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
9
|
-
import { formatCedarCommand } from "@cedarjs/cli-helpers/packageManager/display";
|
|
10
9
|
import {
|
|
11
10
|
addWorkspacePackages,
|
|
12
11
|
removeWorkspacePackages
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
writeFile,
|
|
21
20
|
getDefaultArgs,
|
|
22
21
|
getPaths,
|
|
22
|
+
writeFilesTask,
|
|
23
23
|
addRoutesToRouterTask,
|
|
24
24
|
addScaffoldImport,
|
|
25
25
|
transformTSToJS,
|
|
@@ -31,28 +31,17 @@ import {
|
|
|
31
31
|
} from "../../../lib/rollback.js";
|
|
32
32
|
import { getSchema, verifyModelName } from "../../../lib/schemaHelpers.js";
|
|
33
33
|
import {
|
|
34
|
-
redactedModelFields,
|
|
35
34
|
relationsForModel,
|
|
36
35
|
intForeignKeysForModel,
|
|
37
36
|
mapPrismaScalarToPagePropTsType
|
|
38
37
|
} from "../helpers.js";
|
|
39
38
|
import { builder as sdlBuilder } from "../sdl/sdl.js";
|
|
40
|
-
import {
|
|
41
|
-
files as sdlFiles,
|
|
42
|
-
stubFiles as sdlStubFiles,
|
|
43
|
-
printRedactedFieldsNote,
|
|
44
|
-
redactedSensitiveFields
|
|
45
|
-
} from "../sdl/sdlHandler.js";
|
|
46
|
-
import {
|
|
47
|
-
missingRelatedModels,
|
|
48
|
-
writeFilesWithStubsTask
|
|
49
|
-
} from "../sdl/stubFiles.js";
|
|
39
|
+
import { files as sdlFiles } from "../sdl/sdlHandler.js";
|
|
50
40
|
import { builder as serviceBuilder } from "../service/service.js";
|
|
51
41
|
import { files as serviceFiles } from "../service/serviceHandler.js";
|
|
52
42
|
import { customOrDefaultTemplatePath } from "../yargsHandlerHelpers.js";
|
|
53
43
|
const SKIPPABLE_ASSETS = ["scaffold.css"];
|
|
54
44
|
const PACKAGE_SET = "Set";
|
|
55
|
-
const PACKAGE_PRIVATE_SET = "PrivateSet";
|
|
56
45
|
const getIdType = (model) => {
|
|
57
46
|
return model.fields.find((field) => field.isId)?.type;
|
|
58
47
|
};
|
|
@@ -283,10 +272,7 @@ const modelRelatedVariables = (model) => {
|
|
|
283
272
|
}
|
|
284
273
|
};
|
|
285
274
|
const relations = relationsForModel(model)?.map((relation) => relation);
|
|
286
|
-
const
|
|
287
|
-
model.fields.map((field) => field.name)
|
|
288
|
-
);
|
|
289
|
-
const columns = model.fields.filter((field) => field.kind !== "object").filter((field) => !redactedFields.includes(field.name)).map((column) => {
|
|
275
|
+
const columns = model.fields.filter((field) => field.kind !== "object").map((column) => {
|
|
290
276
|
let validation;
|
|
291
277
|
const meta = componentMetadata[column.type];
|
|
292
278
|
const validationDef = meta?.validation;
|
|
@@ -526,65 +512,6 @@ const addHelperPackages = async (task) => {
|
|
|
526
512
|
await removeWorkspacePackages("web", ["humanize-string"]);
|
|
527
513
|
});
|
|
528
514
|
};
|
|
529
|
-
const isAuthSetup = () => {
|
|
530
|
-
const extensions = ["ts", "js", "tsx", "jsx"];
|
|
531
|
-
return extensions.some(
|
|
532
|
-
(ext) => fs.existsSync(path.join(getPaths().web.src, "auth." + ext))
|
|
533
|
-
);
|
|
534
|
-
};
|
|
535
|
-
const ROUTE_TAG_RE = /<Route\s+([^>]*?)\/?>/g;
|
|
536
|
-
const extractRouteAttr = (tagAttrs, attrName) => tagAttrs.match(new RegExp(`\\b${attrName}=["']([^"']+)["']`))?.[1];
|
|
537
|
-
const getRoutesFileContent = () => {
|
|
538
|
-
const routesPath = getPaths().web.routes;
|
|
539
|
-
if (!fs.existsSync(routesPath)) {
|
|
540
|
-
return void 0;
|
|
541
|
-
}
|
|
542
|
-
return readFile(routesPath).toString();
|
|
543
|
-
};
|
|
544
|
-
const hasLoginRoute = () => {
|
|
545
|
-
const content = getRoutesFileContent();
|
|
546
|
-
if (!content) {
|
|
547
|
-
return false;
|
|
548
|
-
}
|
|
549
|
-
return Array.from(content.matchAll(ROUTE_TAG_RE)).some(
|
|
550
|
-
([, attrs]) => extractRouteAttr(attrs, "name") === "login"
|
|
551
|
-
);
|
|
552
|
-
};
|
|
553
|
-
const findUnprotectedLandingPageRouteName = () => {
|
|
554
|
-
const content = getRoutesFileContent();
|
|
555
|
-
if (!content) {
|
|
556
|
-
return void 0;
|
|
557
|
-
}
|
|
558
|
-
const privateSetRanges = Array.from(
|
|
559
|
-
content.matchAll(/<PrivateSet\b[^>]*>([\s\S]*?)<\/PrivateSet>/g)
|
|
560
|
-
).map((match) => ({
|
|
561
|
-
start: match.index ?? 0,
|
|
562
|
-
end: (match.index ?? 0) + match[0].length
|
|
563
|
-
}));
|
|
564
|
-
for (const match of content.matchAll(ROUTE_TAG_RE)) {
|
|
565
|
-
const [, attrs] = match;
|
|
566
|
-
if (extractRouteAttr(attrs, "path") !== "/") {
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
const tagStart = match.index ?? 0;
|
|
570
|
-
const isProtected = privateSetRanges.some(
|
|
571
|
-
(range) => tagStart >= range.start && tagStart < range.end
|
|
572
|
-
);
|
|
573
|
-
if (!isProtected) {
|
|
574
|
-
return extractRouteAttr(attrs, "name");
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
return void 0;
|
|
578
|
-
};
|
|
579
|
-
const getUnauthenticatedRedirectRoute = () => {
|
|
580
|
-
if (!isAuthSetup()) {
|
|
581
|
-
return void 0;
|
|
582
|
-
}
|
|
583
|
-
if (hasLoginRoute()) {
|
|
584
|
-
return "login";
|
|
585
|
-
}
|
|
586
|
-
return findUnprotectedLandingPageRouteName();
|
|
587
|
-
};
|
|
588
515
|
const addSetImport = (task) => {
|
|
589
516
|
const routesPath = getPaths().web.routes;
|
|
590
517
|
const routesContent = readFile(routesPath).toString();
|
|
@@ -598,19 +525,15 @@ const addSetImport = (task) => {
|
|
|
598
525
|
return void 0;
|
|
599
526
|
}
|
|
600
527
|
const routerImports = importContent.replace(/\s/g, "").split(",");
|
|
601
|
-
|
|
602
|
-
PACKAGE_SET,
|
|
603
|
-
...getUnauthenticatedRedirectRoute() ? [PACKAGE_PRIVATE_SET] : []
|
|
604
|
-
].filter((name) => !routerImports.includes(name));
|
|
605
|
-
if (!namesToImport.length) {
|
|
528
|
+
if (routerImports.includes(PACKAGE_SET)) {
|
|
606
529
|
return "Skipping Set import";
|
|
607
530
|
}
|
|
608
531
|
const newRoutesContent = routesContent.replace(
|
|
609
532
|
cedarRouterImport,
|
|
610
|
-
importStart + spacing +
|
|
533
|
+
importStart + spacing + PACKAGE_SET + `,` + spacing + importContent + importEnd
|
|
611
534
|
);
|
|
612
535
|
writeFile(routesPath, newRoutesContent, { overwriteExisting: true });
|
|
613
|
-
return
|
|
536
|
+
return "Added Set import to Routes.{jsx,tsx}";
|
|
614
537
|
};
|
|
615
538
|
const addScaffoldSetToRouter = async (model, scaffoldPath) => {
|
|
616
539
|
const templateNames = getTemplateStrings(model, scaffoldPath);
|
|
@@ -619,12 +542,10 @@ const addScaffoldSetToRouter = async (model, scaffoldPath) => {
|
|
|
619
542
|
const titleTo = templateNames.pluralRouteName;
|
|
620
543
|
const buttonLabel = `New ${nameVars.singularPascalName}`;
|
|
621
544
|
const buttonTo = templateNames.newRouteName;
|
|
622
|
-
const unauthenticatedRoute = getUnauthenticatedRedirectRoute();
|
|
623
545
|
return addRoutesToRouterTask(
|
|
624
546
|
await routes({ model, path: scaffoldPath }),
|
|
625
547
|
"ScaffoldLayout",
|
|
626
|
-
{ title, titleTo, buttonLabel, buttonTo }
|
|
627
|
-
unauthenticatedRoute ? { unauthenticated: unauthenticatedRoute } : void 0
|
|
548
|
+
{ title, titleTo, buttonLabel, buttonTo }
|
|
628
549
|
);
|
|
629
550
|
};
|
|
630
551
|
const tasks = ({
|
|
@@ -635,27 +556,23 @@ const tasks = ({
|
|
|
635
556
|
tests,
|
|
636
557
|
typescript,
|
|
637
558
|
javascript: _javascript,
|
|
638
|
-
tailwind
|
|
639
|
-
missingModels
|
|
559
|
+
tailwind
|
|
640
560
|
}) => {
|
|
641
561
|
return new Listr(
|
|
642
562
|
[
|
|
643
563
|
{
|
|
644
564
|
title: "Generating scaffold files...",
|
|
645
565
|
task: async () => {
|
|
646
|
-
const f = {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
...await sdlStubFiles(missingModels, model, { docs, typescript })
|
|
657
|
-
};
|
|
658
|
-
return writeFilesWithStubsTask(f, { overwriteExisting: force });
|
|
566
|
+
const f = await files({
|
|
567
|
+
docs,
|
|
568
|
+
model,
|
|
569
|
+
path: path2,
|
|
570
|
+
tests,
|
|
571
|
+
typescript,
|
|
572
|
+
tailwind,
|
|
573
|
+
force
|
|
574
|
+
});
|
|
575
|
+
return writeFilesTask(f, { overwriteExisting: force });
|
|
659
576
|
}
|
|
660
577
|
},
|
|
661
578
|
{
|
|
@@ -720,7 +637,6 @@ const handler = async ({
|
|
|
720
637
|
tailwind = shouldUseTailwindCSS(tailwind);
|
|
721
638
|
try {
|
|
722
639
|
const { name } = await verifyModelName({ name: model });
|
|
723
|
-
const missingModels = await missingRelatedModels(name);
|
|
724
640
|
const t = tasks({
|
|
725
641
|
docs,
|
|
726
642
|
model: name,
|
|
@@ -728,44 +644,12 @@ const handler = async ({
|
|
|
728
644
|
force,
|
|
729
645
|
tests,
|
|
730
646
|
typescript,
|
|
731
|
-
tailwind
|
|
732
|
-
missingModels
|
|
647
|
+
tailwind
|
|
733
648
|
});
|
|
734
649
|
if (rollback && !force) {
|
|
735
650
|
prepareForRollback(t);
|
|
736
651
|
}
|
|
737
652
|
await t.run();
|
|
738
|
-
if (missingModels.length > 0) {
|
|
739
|
-
console.log();
|
|
740
|
-
console.log(
|
|
741
|
-
c.info(
|
|
742
|
-
`${name} has relations to models that don't have SDL files of their own yet: ${missingModels.join(", ")}`
|
|
743
|
-
)
|
|
744
|
-
);
|
|
745
|
-
console.log(
|
|
746
|
-
c.info(
|
|
747
|
-
"Read-only SDL stubs were generated for them, since GraphQL type generation fails otherwise."
|
|
748
|
-
)
|
|
749
|
-
);
|
|
750
|
-
console.log(
|
|
751
|
-
c.info("To replace a stub, run one of the following for each model:")
|
|
752
|
-
);
|
|
753
|
-
for (const stubModel of missingModels) {
|
|
754
|
-
console.log(
|
|
755
|
-
c.info(
|
|
756
|
-
` ${formatCedarCommand(["generate", "sdl", stubModel])} (SDL + service only)`
|
|
757
|
-
)
|
|
758
|
-
);
|
|
759
|
-
console.log(
|
|
760
|
-
c.info(
|
|
761
|
-
` ${formatCedarCommand(["generate", "scaffold", stubModel])} (adds pages, cells, and forms too)`
|
|
762
|
-
)
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
printRedactedFieldsNote(
|
|
767
|
-
await redactedSensitiveFields([name, ...missingModels])
|
|
768
|
-
);
|
|
769
653
|
} catch (e) {
|
|
770
654
|
const message = e instanceof Error ? e.message : String(e);
|
|
771
655
|
const exitCode = e instanceof Error && "exitCode" in e ? e.exitCode ?? 1 : 1;
|
|
@@ -780,10 +664,7 @@ const splitPathAndModel = (pathSlashModel) => {
|
|
|
780
664
|
};
|
|
781
665
|
export {
|
|
782
666
|
files,
|
|
783
|
-
getUnauthenticatedRedirectRoute,
|
|
784
667
|
handler,
|
|
785
|
-
hasLoginRoute,
|
|
786
|
-
isAuthSetup,
|
|
787
668
|
routes,
|
|
788
669
|
shouldUseTailwindCSS,
|
|
789
670
|
splitPathAndModel,
|
|
@@ -3,17 +3,12 @@
|
|
|
3
3
|
// To access your database uncomment the line below
|
|
4
4
|
// import { db } from 'api/src/lib/db'
|
|
5
5
|
|
|
6
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// numeric-looking args are parsed as numbers, others as strings
|
|
10
|
-
_: Array<string | number>
|
|
11
|
-
// named flags, e.g. `--force` -> { force: true }
|
|
12
|
-
[flag: string]: unknown
|
|
13
|
-
}
|
|
6
|
+
interface Args {
|
|
7
|
+
_: string[]
|
|
8
|
+
[key: string]: unknown
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
export default async ({ args }:
|
|
11
|
+
export default async ({ args }: Args) => {
|
|
17
12
|
// Your script here...
|
|
18
13
|
console.log(':: Executing script with args ::')
|
|
19
14
|
console.log(args)
|
|
@@ -3,12 +3,11 @@ import boxen from "boxen";
|
|
|
3
3
|
import camelcase from "camelcase";
|
|
4
4
|
import { Listr } from "listr2";
|
|
5
5
|
import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
6
|
-
import { formatCedarCommand } from "@cedarjs/cli-helpers/packageManager/display";
|
|
7
6
|
import { generate as generateTypes } from "@cedarjs/internal/dist/generate/generate";
|
|
8
7
|
import { getConfig } from "@cedarjs/project-config";
|
|
9
8
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
10
9
|
import { pluralize } from "@cedarjs/utils/cedarPluralize";
|
|
11
|
-
import { transformTSToJS } from "../../../lib/index.js";
|
|
10
|
+
import { transformTSToJS, writeFilesTask } from "../../../lib/index.js";
|
|
12
11
|
import {
|
|
13
12
|
prepareForRollback,
|
|
14
13
|
addFunctionToRollback
|
|
@@ -18,14 +17,9 @@ import {
|
|
|
18
17
|
getEnum,
|
|
19
18
|
verifyModelName
|
|
20
19
|
} from "../../../lib/schemaHelpers.js";
|
|
21
|
-
import {
|
|
20
|
+
import { relationsForModel } from "../helpers.js";
|
|
22
21
|
import { files as serviceFiles } from "../service/serviceHandler.js";
|
|
23
22
|
import { templateForFile } from "../yargsHandlerHelpers.js";
|
|
24
|
-
import {
|
|
25
|
-
addStubHeader,
|
|
26
|
-
missingRelatedModels,
|
|
27
|
-
writeFilesWithStubsTask
|
|
28
|
-
} from "./stubFiles.js";
|
|
29
23
|
const DEFAULT_IGNORE_FIELDS_FOR_INPUT = ["createdAt", "updatedAt"];
|
|
30
24
|
const missingIdConsoleMessage = () => {
|
|
31
25
|
const line1 = ansis.bold.yellow("WARNING") + ": Cannot generate CRUD SDL without an `@id` database column.";
|
|
@@ -52,8 +46,15 @@ const addFieldGraphQLComment = (field, str) => {
|
|
|
52
46
|
const modelFieldToSDL = ({
|
|
53
47
|
field,
|
|
54
48
|
required = true,
|
|
49
|
+
types = {},
|
|
55
50
|
docs = false
|
|
56
51
|
}) => {
|
|
52
|
+
if (Object.entries(types).length && field.kind === "object") {
|
|
53
|
+
const resolvedType = idType(types[field.type]);
|
|
54
|
+
if (typeof resolvedType === "string") {
|
|
55
|
+
field.type = resolvedType;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
57
58
|
const prismaTypeToGraphqlType = {
|
|
58
59
|
Json: "JSON",
|
|
59
60
|
Decimal: "Float",
|
|
@@ -69,37 +70,32 @@ const modelFieldToSDL = ({
|
|
|
69
70
|
return fieldContent;
|
|
70
71
|
}
|
|
71
72
|
};
|
|
72
|
-
const modelRedactedFields = (model) => {
|
|
73
|
-
return redactedModelFields(model.fields.map((field) => field.name));
|
|
74
|
-
};
|
|
75
73
|
const querySDL = (model, docs = false) => {
|
|
76
|
-
|
|
77
|
-
return model.fields.filter((field) => !redactedFields.includes(field.name)).map((field) => modelFieldToSDL({ field, docs }));
|
|
74
|
+
return model.fields.map((field) => modelFieldToSDL({ field, docs }));
|
|
78
75
|
};
|
|
79
|
-
const inputSDL = (model, required, docs = false) => {
|
|
80
|
-
const ignoredFields =
|
|
81
|
-
...DEFAULT_IGNORE_FIELDS_FOR_INPUT,
|
|
82
|
-
...modelRedactedFields(model)
|
|
83
|
-
];
|
|
84
|
-
const idField = model.fields.find((field) => field.isId);
|
|
85
|
-
if (idField?.default !== void 0) {
|
|
86
|
-
ignoredFields.push(idField.name);
|
|
87
|
-
}
|
|
76
|
+
const inputSDL = (model, required, types = {}, docs = false) => {
|
|
77
|
+
const ignoredFields = DEFAULT_IGNORE_FIELDS_FOR_INPUT;
|
|
88
78
|
return model.fields.filter((field) => {
|
|
79
|
+
const idField = model.fields.find((field2) => field2.isId);
|
|
80
|
+
if (idField?.default) {
|
|
81
|
+
ignoredFields.push(idField.name);
|
|
82
|
+
}
|
|
89
83
|
return !ignoredFields.includes(field.name) && field.kind !== "object";
|
|
90
|
-
}).map((field) => modelFieldToSDL({ field, required, docs }));
|
|
84
|
+
}).map((field) => modelFieldToSDL({ field, required, types, docs }));
|
|
91
85
|
};
|
|
92
86
|
function idInputSDL(idType2, docs) {
|
|
93
87
|
if (!Array.isArray(idType2)) {
|
|
94
88
|
return [];
|
|
95
89
|
}
|
|
96
|
-
return idType2.map(
|
|
90
|
+
return idType2.map(
|
|
91
|
+
(field) => modelFieldToSDL({ field, required: true, types: {}, docs })
|
|
92
|
+
);
|
|
97
93
|
}
|
|
98
|
-
const createInputSDL = (model, docs = false) => {
|
|
99
|
-
return inputSDL(model, true, docs);
|
|
94
|
+
const createInputSDL = (model, types = {}, docs = false) => {
|
|
95
|
+
return inputSDL(model, true, types, docs);
|
|
100
96
|
};
|
|
101
|
-
const updateInputSDL = (model, docs = false) => {
|
|
102
|
-
return inputSDL(model, false, docs);
|
|
97
|
+
const updateInputSDL = (model, types = {}, docs = false) => {
|
|
98
|
+
return inputSDL(model, false, types, docs);
|
|
103
99
|
};
|
|
104
100
|
function idType(model, crud) {
|
|
105
101
|
if (!crud || !model) {
|
|
@@ -135,6 +131,21 @@ const sdlFromSchemaModel = async (name, crud, docs = false) => {
|
|
|
135
131
|
);
|
|
136
132
|
}
|
|
137
133
|
const model = schemaResult;
|
|
134
|
+
const resolvedTypes = await Promise.all(
|
|
135
|
+
model.fields.filter((field) => field.kind === "object").map(async (field) => {
|
|
136
|
+
const fieldModel = await getSchema(field.type);
|
|
137
|
+
return fieldModel && "fields" in fieldModel ? fieldModel : void 0;
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
const types = resolvedTypes.reduce(
|
|
141
|
+
(acc, cur) => {
|
|
142
|
+
if (!cur) {
|
|
143
|
+
return acc;
|
|
144
|
+
}
|
|
145
|
+
return { ...acc, [cur.name]: cur };
|
|
146
|
+
},
|
|
147
|
+
{}
|
|
148
|
+
);
|
|
138
149
|
const enums = (await Promise.all(
|
|
139
150
|
model.fields.filter((field) => field.kind === "enum").map(async (field) => {
|
|
140
151
|
const enumDef = await getEnum(field.type);
|
|
@@ -148,8 +159,8 @@ const sdlFromSchemaModel = async (name, crud, docs = false) => {
|
|
|
148
159
|
modelName,
|
|
149
160
|
modelDescription,
|
|
150
161
|
query: querySDL(model, docs).join("\n "),
|
|
151
|
-
createInput: createInputSDL(model, docs).join("\n "),
|
|
152
|
-
updateInput: updateInputSDL(model, docs).join("\n "),
|
|
162
|
+
createInput: createInputSDL(model, types, docs).join("\n "),
|
|
163
|
+
updateInput: updateInputSDL(model, types, docs).join("\n "),
|
|
153
164
|
idInput: idInputSDL(idTypeRes, docs).join("\n "),
|
|
154
165
|
idType: idType(model, crud),
|
|
155
166
|
idName: idName(model, crud),
|
|
@@ -187,55 +198,6 @@ const files = async ({
|
|
|
187
198
|
})
|
|
188
199
|
};
|
|
189
200
|
};
|
|
190
|
-
const stubFiles = async (models, generatedFor, { docs = false, typescript }) => {
|
|
191
|
-
const generatedFiles = {};
|
|
192
|
-
for (const stubModel of models) {
|
|
193
|
-
const stubModelFiles = await files({
|
|
194
|
-
name: stubModel,
|
|
195
|
-
crud: false,
|
|
196
|
-
docs,
|
|
197
|
-
tests: false,
|
|
198
|
-
typescript
|
|
199
|
-
});
|
|
200
|
-
for (const [stubPath, stubContent] of Object.entries(stubModelFiles)) {
|
|
201
|
-
generatedFiles[stubPath] = addStubHeader({
|
|
202
|
-
content: stubContent,
|
|
203
|
-
stubModel,
|
|
204
|
-
generatedFor
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return generatedFiles;
|
|
209
|
-
};
|
|
210
|
-
const redactedSensitiveFields = async (modelNames) => {
|
|
211
|
-
const redacted = [];
|
|
212
|
-
for (const modelName of modelNames) {
|
|
213
|
-
const model = await getSchema(modelName);
|
|
214
|
-
if (!model || !("fields" in model)) {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
for (const fieldName of modelRedactedFields(model)) {
|
|
218
|
-
redacted.push(`${model.name}.${fieldName}`);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return redacted;
|
|
222
|
-
};
|
|
223
|
-
const printRedactedFieldsNote = (redactedFields) => {
|
|
224
|
-
if (redactedFields.length === 0) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
console.log();
|
|
228
|
-
console.log(
|
|
229
|
-
c.warning(
|
|
230
|
-
`The following fields were excluded from the generated SDL because they may contain sensitive data: ${redactedFields.join(", ")}`
|
|
231
|
-
)
|
|
232
|
-
);
|
|
233
|
-
console.log(
|
|
234
|
-
c.warning(
|
|
235
|
-
"If you want to expose any of them through your GraphQL API you can add them to the SDL file manually."
|
|
236
|
-
)
|
|
237
|
-
);
|
|
238
|
-
};
|
|
239
201
|
const handler = async ({
|
|
240
202
|
model,
|
|
241
203
|
crud,
|
|
@@ -259,21 +221,13 @@ const handler = async ({
|
|
|
259
221
|
});
|
|
260
222
|
try {
|
|
261
223
|
const { name } = await verifyModelName({ name: model });
|
|
262
|
-
const missingModels = await missingRelatedModels(name);
|
|
263
|
-
const redactedFields = await redactedSensitiveFields([
|
|
264
|
-
name,
|
|
265
|
-
...missingModels
|
|
266
|
-
]);
|
|
267
224
|
const tasks = new Listr(
|
|
268
225
|
[
|
|
269
226
|
{
|
|
270
227
|
title: "Generating SDL files...",
|
|
271
228
|
task: async () => {
|
|
272
|
-
const f = {
|
|
273
|
-
|
|
274
|
-
...await stubFiles(missingModels, name, { typescript, docs })
|
|
275
|
-
};
|
|
276
|
-
return writeFilesWithStubsTask(f, { overwriteExisting: force });
|
|
229
|
+
const f = await files({ name, tests, crud, typescript, docs });
|
|
230
|
+
return writeFilesTask(f, { overwriteExisting: force });
|
|
277
231
|
}
|
|
278
232
|
},
|
|
279
233
|
{
|
|
@@ -300,26 +254,6 @@ const handler = async ({
|
|
|
300
254
|
prepareForRollback(tasks);
|
|
301
255
|
}
|
|
302
256
|
await tasks.run();
|
|
303
|
-
if (missingModels.length > 0) {
|
|
304
|
-
console.log();
|
|
305
|
-
console.log(
|
|
306
|
-
c.info(
|
|
307
|
-
`${name} has relations to models that don't have SDL files of their own yet: ${missingModels.join(", ")}`
|
|
308
|
-
)
|
|
309
|
-
);
|
|
310
|
-
console.log(
|
|
311
|
-
c.info(
|
|
312
|
-
"Read-only SDL stubs were generated for them, since GraphQL type generation fails otherwise."
|
|
313
|
-
)
|
|
314
|
-
);
|
|
315
|
-
console.log(c.info("To replace a stub with a full SDL and service, run"));
|
|
316
|
-
for (const stubModel of missingModels) {
|
|
317
|
-
console.log(
|
|
318
|
-
c.info(` ${formatCedarCommand(["generate", "sdl", stubModel])}`)
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
printRedactedFieldsNote(redactedFields);
|
|
323
257
|
} catch (e) {
|
|
324
258
|
const message = e instanceof Error ? e.message : String(e);
|
|
325
259
|
const exitCode = e instanceof Error && "exitCode" in e && typeof e.exitCode === "number" ? e.exitCode : 1;
|
|
@@ -330,8 +264,5 @@ const handler = async ({
|
|
|
330
264
|
};
|
|
331
265
|
export {
|
|
332
266
|
files,
|
|
333
|
-
handler
|
|
334
|
-
printRedactedFieldsNote,
|
|
335
|
-
redactedSensitiveFields,
|
|
336
|
-
stubFiles
|
|
267
|
+
handler
|
|
337
268
|
};
|
|
@@ -30,7 +30,6 @@ export const schema = gql`
|
|
|
30
30
|
<% } %>${singularCamelName}(${idName}: ${idType}!): ${singularPascalName} @requireAuth<% } %>
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
<% if (crud) { %>
|
|
34
33
|
<% if (docs) { %>
|
|
35
34
|
"""Autogenerated input type of Input${singularPascalName}."""
|
|
36
35
|
<% } %>
|
|
@@ -52,7 +51,7 @@ export const schema = gql`
|
|
|
52
51
|
<% } %>
|
|
53
52
|
input Update${singularPascalName}Input {
|
|
54
53
|
${updateInput}
|
|
55
|
-
}
|
|
54
|
+
}<% if (crud) { %>
|
|
56
55
|
|
|
57
56
|
<% if (docs) { %>
|
|
58
57
|
"""About mutations"""
|
|
@@ -30,7 +30,6 @@ export const schema = gql`
|
|
|
30
30
|
<% } %>${singularCamelName}(${idName}: ${idType}!): ${singularPascalName} @requireAuth<% } %>
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
<% if (crud) { %>
|
|
34
33
|
<% if (docs) { %>
|
|
35
34
|
"""Autogenerated input type of Input${singularPascalName}."""
|
|
36
35
|
<% } %>
|
|
@@ -52,7 +51,7 @@ export const schema = gql`
|
|
|
52
51
|
<% } %>
|
|
53
52
|
input Update${singularPascalName}Input {
|
|
54
53
|
${updateInput}
|
|
55
|
-
}
|
|
54
|
+
}<% if (crud) { %>
|
|
56
55
|
|
|
57
56
|
<% if (docs) { %>
|
|
58
57
|
"""About mutations"""
|
|
@@ -3,7 +3,7 @@ import camelcase from "camelcase";
|
|
|
3
3
|
import { pluralize, singularize } from "@cedarjs/utils/cedarPluralize";
|
|
4
4
|
import { transformTSToJS } from "../../../lib/index.js";
|
|
5
5
|
import { getSchema, verifyModelName } from "../../../lib/schemaHelpers.js";
|
|
6
|
-
import {
|
|
6
|
+
import { relationsForModel } from "../helpers.js";
|
|
7
7
|
import { createHandler, templateForFile } from "../yargsHandlerHelpers.js";
|
|
8
8
|
function isServiceModel(schema) {
|
|
9
9
|
return typeof schema === "object" && schema !== null && "fields" in schema && "name" in schema;
|
|
@@ -32,10 +32,7 @@ const parseSchema = async (model) => {
|
|
|
32
32
|
return field.isRequired && !field.hasDefaultValue && // don't include fields that the database will default
|
|
33
33
|
!field.relationName;
|
|
34
34
|
});
|
|
35
|
-
|
|
36
|
-
schema.fields.map((field) => field.name)
|
|
37
|
-
);
|
|
38
|
-
return { scalarFields, relations, foreignKeys, redactedFields };
|
|
35
|
+
return { scalarFields, relations, foreignKeys };
|
|
39
36
|
};
|
|
40
37
|
function scenarioFieldValue(field) {
|
|
41
38
|
const randFloat = Math.random() * 1e7;
|
|
@@ -148,14 +145,9 @@ const fieldTypes = async (model) => {
|
|
|
148
145
|
);
|
|
149
146
|
};
|
|
150
147
|
const fieldsToInput = async (model) => {
|
|
151
|
-
const { scalarFields, foreignKeys
|
|
148
|
+
const { scalarFields, foreignKeys } = await parseSchema(model);
|
|
152
149
|
const modelName = camelcase(singularize(model));
|
|
153
150
|
const inputObj = {};
|
|
154
|
-
if (scalarFields.some(
|
|
155
|
-
(field) => redactedFields.includes(field.name)
|
|
156
|
-
)) {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
151
|
scalarFields.forEach((field) => {
|
|
160
152
|
if (foreignKeys.includes(field.name)) {
|
|
161
153
|
inputObj[field.name] = `scenario.${modelName}.two.${field.name}`;
|
|
@@ -170,17 +162,14 @@ const fieldsToInput = async (model) => {
|
|
|
170
162
|
}
|
|
171
163
|
};
|
|
172
164
|
const fieldsToUpdate = async (model) => {
|
|
173
|
-
const { scalarFields, relations, foreignKeys
|
|
165
|
+
const { scalarFields, relations, foreignKeys } = await parseSchema(model);
|
|
174
166
|
const modelName = camelcase(singularize(model));
|
|
175
167
|
let field, newValue, fieldName;
|
|
176
|
-
|
|
177
|
-
(scalar) => !redactedFields.includes(scalar.name)
|
|
178
|
-
);
|
|
179
|
-
field = updatableFields.find(
|
|
168
|
+
field = scalarFields.find(
|
|
180
169
|
(scalar) => !foreignKeys.includes(scalar.name)
|
|
181
170
|
);
|
|
182
171
|
if (!field) {
|
|
183
|
-
field =
|
|
172
|
+
field = scalarFields[0];
|
|
184
173
|
}
|
|
185
174
|
if (!field) {
|
|
186
175
|
return false;
|
|
@@ -30,8 +30,14 @@ const customOrDefaultTemplatePath = ({
|
|
|
30
30
|
generator,
|
|
31
31
|
templatePath
|
|
32
32
|
);
|
|
33
|
+
const deprecatedCustomPath = getPaths()[side].generators ? path.join(getPaths()[side].generators, generator, templatePath) : void 0;
|
|
33
34
|
if (fs.existsSync(customPath)) {
|
|
34
35
|
return customPath;
|
|
36
|
+
} else if (deprecatedCustomPath && fs.existsSync(deprecatedCustomPath)) {
|
|
37
|
+
console.log(
|
|
38
|
+
`Having generator templates in ${getPaths()[side].generators} has been deprecated. Please move them to ${getPaths().generatorTemplates}.`
|
|
39
|
+
);
|
|
40
|
+
return deprecatedCustomPath;
|
|
35
41
|
} else {
|
|
36
42
|
return defaultPath;
|
|
37
43
|
}
|