@cedarjs/cli 7.0.0 → 8.0.0-canary.3265
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/build/buildHandler.js +2 -1
- package/dist/commands/build.js +1 -1
- package/dist/commands/check.js +2 -1
- package/dist/commands/deploy/baremetal/baremetalHandler.js +1 -1
- package/dist/commands/deploy/baremetal.js +1 -1
- package/dist/commands/deploy/flightcontrolHandler.js +1 -1
- package/dist/commands/deploy/helpers/deployHandler.js +1 -1
- package/dist/commands/deploy/netlify.js +1 -1
- package/dist/commands/deploy/renderHandler.js +1 -1
- package/dist/commands/deploy/serverlessHandler.js +2 -1
- package/dist/commands/deploy/vercel.js +1 -1
- package/dist/commands/destroy/handlerHelpers.js +1 -1
- package/dist/commands/destroy/page/pageHandler.js +2 -1
- package/dist/commands/destroy/scaffold/scaffoldHandler.js +2 -1
- package/dist/commands/destroy/sdl/sdlHandler.js +2 -1
- package/dist/commands/dev/devHandler.js +3 -4
- package/dist/commands/dev/packageWatchCommands.js +8 -3
- package/dist/commands/dev.js +1 -1
- package/dist/commands/execHandler.js +2 -1
- package/dist/commands/experimental/live-queries/liveQueries.js +1 -1
- package/dist/commands/experimental/live-queries/liveQueriesHandler.js +3 -3
- package/dist/commands/experimental/setupInngest.js +1 -1
- package/dist/commands/experimental/setupInngestHandler.js +1 -1
- package/dist/commands/experimental/setupOpentelemetry.js +1 -1
- package/dist/commands/experimental/setupOpentelemetryHandler.js +2 -1
- package/dist/commands/experimental/setupReactCompiler.js +1 -1
- package/dist/commands/experimental/setupReactCompilerHandler.js +4 -4
- package/dist/commands/experimental/setupRsc.js +1 -1
- package/dist/commands/experimental/setupRscHandler.js +2 -1
- package/dist/commands/experimental/setupStreamingSsr.js +1 -1
- package/dist/commands/experimental/setupStreamingSsrHandler.js +3 -2
- package/dist/commands/generate/cell/cell.js +20 -0
- package/dist/commands/generate/cell/cellHandler.js +68 -20
- package/dist/commands/generate/cell/templates/cell.tsx.template +21 -2
- package/dist/commands/generate/cell/templates/cellFragment.tsx.template +43 -0
- package/dist/commands/generate/cell/templates/cellList.tsx.template +21 -2
- package/dist/commands/generate/cell/templates/storiesFragment.tsx.template +23 -0
- package/dist/commands/generate/cell/templates/test.js.template +1 -1
- package/dist/commands/generate/cell/templates/testFragment.js.template +30 -0
- package/dist/commands/generate/dataMigration/dataMigration.js +2 -1
- package/dist/commands/generate/dbAuth/dbAuthHandler.js +23 -3
- package/dist/commands/generate/dbAuth/templates/login.oauth.tsx.template +164 -0
- package/dist/commands/generate/dbAuth/templates/signup.oauth.tsx.template +147 -0
- package/dist/commands/generate/directive/directiveHandler.js +2 -1
- package/dist/commands/generate/function/functionHandler.js +2 -1
- package/dist/commands/generate/job/jobHandler.js +2 -1
- package/dist/commands/generate/model/modelHandler.js +2 -1
- package/dist/commands/generate/ogImage/ogImageHandler.js +2 -1
- package/dist/commands/generate/package/filesTask.js +9 -12
- package/dist/commands/generate/package/packageHandler.js +3 -4
- package/dist/commands/generate/page/pageHandler.js +2 -1
- package/dist/commands/generate/realtime/realtime.js +1 -1
- package/dist/commands/generate/realtime/realtimeHandler.js +2 -9
- package/dist/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template +1 -1
- package/dist/commands/generate/scaffold/scaffoldHandler.js +10 -5
- package/dist/commands/generate/scaffold/templates/components/NameForm.tsx.template +3 -3
- package/dist/commands/generate/scaffold/templates/lib/formatters.test.tsx.template +11 -0
- package/dist/commands/generate/scaffold/templates/lib/formatters.tsx.template +13 -1
- package/dist/commands/generate/script/scriptHandler.js +2 -1
- package/dist/commands/generate/sdl/sdlHandler.js +2 -1
- package/dist/commands/generate/secret/secret.js +1 -1
- package/dist/commands/generate/service/serviceHandler.js +47 -38
- package/dist/commands/generate/service/templates/service.ts.template +22 -8
- package/dist/commands/generate/service/templates/test.ts.template +1 -1
- package/dist/commands/generate/yargsCommandHelpers.js +1 -1
- package/dist/commands/generate/yargsHandlerHelpers.js +2 -1
- package/dist/commands/generate.js +1 -1
- package/dist/commands/info.js +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/prerenderHandler.js +11 -4
- package/dist/commands/prismaHandler.js +7 -2
- package/dist/commands/record/init.js +1 -1
- package/dist/commands/serve.js +2 -1
- package/dist/commands/setup/auth/auth.js +2 -4
- package/dist/commands/setup/cache/cache.js +1 -1
- package/dist/commands/setup/cache/cacheHandler.js +2 -1
- package/dist/commands/setup/database/postgres.js +1 -1
- package/dist/commands/setup/database/postgresHandler.js +3 -1
- package/dist/commands/setup/deploy/providers/baremetalHandler.js +2 -1
- package/dist/commands/setup/deploy/providers/coherence.js +1 -1
- package/dist/commands/setup/deploy/providers/coherenceHandler.js +7 -6
- package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +2 -1
- package/dist/commands/setup/deploy/providers/netlifyHandler.js +2 -1
- package/dist/commands/setup/deploy/providers/renderHandler.js +2 -1
- package/dist/commands/setup/deploy/providers/serverlessHandler.js +2 -1
- package/dist/commands/setup/deploy/providers/universalDeployHandler.js +4 -6
- package/dist/commands/setup/deploy/providers/vercelHandler.js +2 -1
- package/dist/commands/setup/docker/docker.js +1 -1
- package/dist/commands/setup/docker/dockerHandler.js +2 -1
- package/dist/commands/setup/generator/generator.js +1 -1
- package/dist/commands/setup/generator/generatorHandler.js +1 -1
- package/dist/commands/setup/graphql/features/fragments/fragments.js +1 -1
- package/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js +3 -5
- package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocuments.js +1 -1
- package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js +2 -1
- package/dist/commands/setup/i18n/i18n.js +1 -1
- package/dist/commands/setup/i18n/i18nHandler.js +1 -1
- package/dist/commands/setup/jobs/jobs.js +1 -1
- package/dist/commands/setup/jobs/jobsHandler.js +2 -1
- package/dist/commands/setup/mailer/mailer.js +1 -1
- package/dist/commands/setup/mailer/mailerHandler.js +2 -1
- package/dist/commands/setup/middleware/ogImage/ogImage.js +1 -1
- package/dist/commands/setup/middleware/ogImage/ogImageHandler.js +2 -1
- package/dist/commands/setup/monitoring/sentry/sentry.js +1 -1
- package/dist/commands/setup/monitoring/sentry/sentryHandler.js +8 -7
- package/dist/commands/setup/neon/neon.js +1 -1
- package/dist/commands/setup/neon/neonHandler.js +5 -9
- package/dist/commands/setup/package/package.js +1 -1
- package/dist/commands/setup/package/packageHandler.js +4 -4
- package/dist/commands/setup/realtime/addRealtimeToGraphql.js +1 -1
- package/dist/commands/setup/realtime/realtime.js +1 -1
- package/dist/commands/setup/realtime/realtimeHandler.js +4 -16
- package/dist/commands/setup/realtime/templates/subscriptions/countdown/countdown.ts.template +1 -1
- package/dist/commands/setup/realtime/templates/subscriptions/newMessage/newMessage.ts.template +1 -1
- package/dist/commands/setup/server-file/serverFile.js +1 -1
- package/dist/commands/setup/server-file/serverFileHandler.js +2 -1
- package/dist/commands/setup/tenancy/authCodemod.js +66 -0
- package/dist/commands/setup/tenancy/dbCodemod.js +64 -0
- package/dist/commands/setup/tenancy/graphqlCodemod.js +88 -0
- package/dist/commands/setup/tenancy/preflight.js +49 -0
- package/dist/commands/setup/tenancy/schemaPrisma.js +81 -0
- package/dist/commands/setup/tenancy/signupCodemod.js +153 -0
- package/dist/commands/setup/tenancy/templates/InvitePage.tsx.template +63 -0
- package/dist/commands/setup/tenancy/templates/OrgScope.tsx.template +40 -0
- package/dist/commands/setup/tenancy/templates/ensureDefaultOrganizations.dataMigration.ts.template +39 -0
- package/dist/commands/setup/tenancy/templates/organizations.scenarios.ts.template +59 -0
- package/dist/commands/setup/tenancy/templates/organizations.sdl.ts.template +89 -0
- package/dist/commands/setup/tenancy/templates/organizations.test.ts.template +389 -0
- package/dist/commands/setup/tenancy/templates/organizations.ts.template +394 -0
- package/dist/commands/setup/tenancy/templates/requireMembership.test.ts.template +35 -0
- package/dist/commands/setup/tenancy/templates/requireMembership.ts.template +25 -0
- package/dist/commands/setup/tenancy/tenancy.js +30 -0
- package/dist/commands/setup/tenancy/tenancyHandler.js +425 -0
- package/dist/commands/setup/ui/libraries/chakra-uiHandler.js +2 -1
- package/dist/commands/setup/ui/libraries/mantineHandler.js +2 -1
- package/dist/commands/setup/ui/libraries/tailwindcssHandler.js +2 -1
- package/dist/commands/setup/uploads/schemaPrisma.js +41 -0
- package/dist/commands/setup/uploads/serverFile.js +151 -0
- package/dist/commands/setup/uploads/templates/requireUploadToken.ts.template +15 -0
- package/dist/commands/setup/uploads/templates/srcLibUploads.ts.template +23 -24
- package/dist/commands/setup/uploads/templates/uploads.sdl.ts.template +79 -0
- package/dist/commands/setup/uploads/templates/uploads.ts.template +89 -0
- package/dist/commands/setup/uploads/templates/withDataUri.ts.template +14 -0
- package/dist/commands/setup/uploads/templates/withSignedUrl.ts.template +16 -0
- package/dist/commands/setup/uploads/uploads.js +49 -7
- package/dist/commands/setup/uploads/uploadsHandler.js +256 -69
- package/dist/commands/setup.js +2 -1
- package/dist/commands/studio.js +1 -1
- package/dist/commands/test/testHandler.js +25 -71
- package/dist/commands/test/viteVersionCheck.js +75 -0
- package/dist/commands/test.js +10 -18
- package/dist/commands/type-checkHandler.js +1 -1
- package/dist/commands/upgrade/preUpgradeScripts.js +3 -3
- package/dist/commands/upgrade/upgrade.js +1 -1
- package/dist/commands/upgrade/upgradeHandler.js +2 -1
- package/dist/index.js +5 -8
- package/dist/lib/exit.js +1 -1
- package/dist/lib/index.js +6 -25
- package/dist/lib/loadEnvFiles.js +1 -1
- package/dist/lib/plugin.js +1 -1
- package/dist/lib/test.js +1 -1
- package/dist/lib/updateCheck.js +4 -4
- package/dist/middleware/checkNodeVersion.js +4 -4
- package/package.json +19 -19
- package/dist/commands/setup/uploads/dbCodemod.js +0 -28
- package/dist/commands/setup/uploads/templates/signedUrl.ts.template +0 -21
- package/dist/commands/test/testHandlerEsm.js +0 -109
- package/dist/commands/testEsm.js +0 -41
|
@@ -1,93 +1,265 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
3
4
|
import { Listr } from "listr2";
|
|
4
5
|
import { format } from "prettier";
|
|
5
6
|
import { terminalLink } from "termi-link";
|
|
7
|
+
import { colors as c } from "@cedarjs/cli-helpers/colors";
|
|
8
|
+
import { getPrettierOptions } from "@cedarjs/cli-helpers/fileHelpers";
|
|
6
9
|
import {
|
|
7
10
|
addApiPackages,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "@cedarjs/cli-helpers";
|
|
11
|
+
addWebPackages
|
|
12
|
+
} from "@cedarjs/cli-helpers/installHelpers";
|
|
11
13
|
import { formatCedarCommand } from "@cedarjs/cli-helpers/packageManager/display";
|
|
14
|
+
import { addEnvVar } from "@cedarjs/cli-helpers/project";
|
|
15
|
+
import { getSchemaPath } from "@cedarjs/project-config";
|
|
12
16
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
13
17
|
import { getPaths, transformTSToJS, writeFile } from "../../../lib/index.js";
|
|
14
18
|
import { isTypeScriptProject } from "../../../lib/project.js";
|
|
15
|
-
import {
|
|
16
|
-
|
|
19
|
+
import { generateSecret } from "../../generate/secret/secret.js";
|
|
20
|
+
import { setupServerFileTasks } from "../server-file/serverFileHandler.js";
|
|
21
|
+
import { addUploadModel } from "./schemaPrisma.js";
|
|
22
|
+
import {
|
|
23
|
+
addUploadsPlugin,
|
|
24
|
+
detectServerAuth,
|
|
25
|
+
hasUploadsPlugin
|
|
26
|
+
} from "./serverFile.js";
|
|
27
|
+
const UPPY_VERSION = "^6.0.0";
|
|
28
|
+
const S3_IMPORTS = `import { S3Client } from '@aws-sdk/client-s3'
|
|
29
|
+
|
|
30
|
+
import { createS3Provider } from '@cedarjs/uploads/s3'
|
|
31
|
+
`;
|
|
32
|
+
const S3_CLIENT = `
|
|
33
|
+
// Your own S3 client. It stays available for S3-specific work (lifecycle
|
|
34
|
+
// rules, CORS, and so on) alongside the storage target below. Credentials
|
|
35
|
+
// come from the SDK's default chain: AWS_ACCESS_KEY_ID and
|
|
36
|
+
// AWS_SECRET_ACCESS_KEY in .env, or an IAM role, SSO, or container
|
|
37
|
+
// credentials in production. For an S3-compatible service, add \`endpoint\`
|
|
38
|
+
// and \`forcePathStyle: true\`.
|
|
39
|
+
export const s3Client = new S3Client({
|
|
40
|
+
region: process.env.AWS_REGION,
|
|
41
|
+
})
|
|
42
|
+
`;
|
|
43
|
+
const TARGET_BLOCKS = {
|
|
44
|
+
fs: ` // Files uploaded through the api server and stored on local disk. Good for
|
|
45
|
+
// development and single-server deploys.
|
|
46
|
+
local: createFsProvider({
|
|
47
|
+
uploadDir: path.join(getPaths().api.base, '.uploads'),
|
|
48
|
+
serveBaseUrl: process.env.UPLOAD_SERVE_BASE_URL,
|
|
49
|
+
signSecret: process.env.UPLOAD_TOKEN_SECRET,
|
|
50
|
+
}),
|
|
51
|
+
`,
|
|
52
|
+
db: ` // Small files stored inline in the Upload row (avatars, thumbnails).
|
|
53
|
+
thumbnails: createDbProvider(),
|
|
54
|
+
`,
|
|
55
|
+
s3: ` // Files uploaded straight from the browser to S3 with presigned URLs.
|
|
56
|
+
files: createS3Provider({
|
|
57
|
+
client: s3Client,
|
|
58
|
+
bucket: process.env.S3_BUCKET_UPLOADS,
|
|
59
|
+
region: process.env.AWS_REGION,
|
|
60
|
+
keyPrefix: 'uploads/',
|
|
61
|
+
}),
|
|
62
|
+
`
|
|
63
|
+
};
|
|
64
|
+
const PROFILE_BLOCKS = {
|
|
65
|
+
fs: ` attachment: {
|
|
66
|
+
target: 'local',
|
|
67
|
+
allowedMimeTypes: ['application/pdf', 'image/png', 'image/jpeg'],
|
|
68
|
+
maxFileSize: 25 * 1024 * 1024,
|
|
69
|
+
maxFiles: 10,
|
|
70
|
+
},
|
|
71
|
+
`,
|
|
72
|
+
db: ` avatar: {
|
|
73
|
+
target: 'thumbnails',
|
|
74
|
+
allowedMimeTypes: ['image/png', 'image/jpeg'],
|
|
75
|
+
maxFileSize: DB_MAX_FILE_SIZE,
|
|
76
|
+
maxFiles: 1,
|
|
77
|
+
},
|
|
78
|
+
`,
|
|
79
|
+
s3: ` document: {
|
|
80
|
+
target: 'files',
|
|
81
|
+
allowedMimeTypes: ['application/pdf', 'image/*'],
|
|
82
|
+
maxFileSize: 100 * 1024 * 1024,
|
|
83
|
+
maxFiles: 5,
|
|
84
|
+
},
|
|
85
|
+
`
|
|
86
|
+
};
|
|
87
|
+
const handler = async ({ targets, force }) => {
|
|
88
|
+
const paths = getPaths();
|
|
17
89
|
const projectIsTypescript = isTypeScriptProject();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
90
|
+
const ext = projectIsTypescript ? "ts" : "js";
|
|
91
|
+
const wantsFs = targets.includes("fs");
|
|
92
|
+
const wantsDb = targets.includes("db");
|
|
93
|
+
const wantsS3 = targets.includes("s3");
|
|
94
|
+
const wantsUppy = wantsFs || wantsS3;
|
|
95
|
+
const packageJson = await import(pathToFileURL(path.join(paths.base, "package.json")).href, { with: { type: "json" } });
|
|
21
96
|
const cedarVersion = packageJson.default.devDependencies["@cedarjs/core"] ?? "latest";
|
|
97
|
+
const serverFilePath = path.join(paths.api.src, `server.${ext}`);
|
|
98
|
+
const writeTemplateFile = async (templateFile, outputPath, replacements = {}) => {
|
|
99
|
+
const templatePath = path.resolve(
|
|
100
|
+
import.meta.dirname,
|
|
101
|
+
"templates",
|
|
102
|
+
templateFile
|
|
103
|
+
);
|
|
104
|
+
let templateContent = fs.readFileSync(templatePath, "utf-8");
|
|
105
|
+
for (const [token, value] of Object.entries(replacements)) {
|
|
106
|
+
templateContent = templateContent.replaceAll(token, value);
|
|
107
|
+
}
|
|
108
|
+
const content = projectIsTypescript ? templateContent : await transformTSToJS(outputPath, templateContent);
|
|
109
|
+
return writeFile(outputPath, content, { overwriteExisting: force });
|
|
110
|
+
};
|
|
111
|
+
const apiPackages = [`@cedarjs/uploads@${cedarVersion}`];
|
|
112
|
+
const webPackages = [`@cedarjs/uploads@${cedarVersion}`];
|
|
113
|
+
if (wantsS3) {
|
|
114
|
+
apiPackages.push("@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner");
|
|
115
|
+
}
|
|
116
|
+
if (wantsUppy) {
|
|
117
|
+
webPackages.push(
|
|
118
|
+
`@uppy/core@${UPPY_VERSION}`,
|
|
119
|
+
`@uppy/react@${UPPY_VERSION}`,
|
|
120
|
+
`@uppy/dashboard@${UPPY_VERSION}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (wantsFs) {
|
|
124
|
+
webPackages.push(`@uppy/xhr-upload@${UPPY_VERSION}`);
|
|
125
|
+
}
|
|
126
|
+
if (wantsS3) {
|
|
127
|
+
webPackages.push(`@uppy/aws-s3@${UPPY_VERSION}`);
|
|
128
|
+
}
|
|
22
129
|
const tasks = new Listr(
|
|
23
130
|
[
|
|
24
131
|
{
|
|
25
|
-
|
|
132
|
+
...addApiPackages(apiPackages),
|
|
133
|
+
title: "Adding @cedarjs/uploads to your api side..."
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
...addWebPackages(webPackages),
|
|
137
|
+
title: "Adding @cedarjs/uploads to your web side..."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
title: "Adding the Upload model to schema.prisma...",
|
|
26
141
|
task: async () => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
142
|
+
const schemaPath = await getSchemaPath(paths.api.prismaConfig);
|
|
143
|
+
const schema = fs.readFileSync(schemaPath, "utf-8");
|
|
144
|
+
fs.writeFileSync(schemaPath, addUploadModel(schema));
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
title: `Adding api/src/lib/uploads.${ext}...`,
|
|
149
|
+
task: async () => {
|
|
150
|
+
await writeTemplateFile(
|
|
151
|
+
"srcLibUploads.ts.template",
|
|
152
|
+
path.join(paths.api.lib, `uploads.${ext}`),
|
|
153
|
+
{
|
|
154
|
+
__S3_IMPORTS__: wantsS3 ? S3_IMPORTS : "",
|
|
155
|
+
__S3_CLIENT__: wantsS3 ? S3_CLIENT : "",
|
|
156
|
+
__FS_IMPORT__: wantsFs ? "createFsProvider," : "",
|
|
157
|
+
__DB_IMPORT__: wantsDb ? "createDbProvider," : "",
|
|
158
|
+
__DB_MAX_IMPORT__: wantsDb ? "DB_MAX_FILE_SIZE," : "",
|
|
159
|
+
__PATH_IMPORT__: wantsFs ? "import path from 'node:path'\n\n" : "",
|
|
160
|
+
__GET_PATHS_IMPORT__: wantsFs ? "import { getPaths } from '@cedarjs/project-config'\n" : "",
|
|
161
|
+
__TARGETS__: targets.map((t) => TARGET_BLOCKS[t]).join("\n"),
|
|
162
|
+
__PROFILES__: targets.map((t) => PROFILE_BLOCKS[t]).join("\n")
|
|
163
|
+
}
|
|
39
164
|
);
|
|
40
|
-
const uploadsContent = projectIsTypescript ? templateContent : await transformTSToJS(uploadsPath, templateContent);
|
|
41
|
-
return writeFile(uploadsPath, uploadsContent, {
|
|
42
|
-
overwriteExisting: force
|
|
43
|
-
});
|
|
44
165
|
}
|
|
45
166
|
},
|
|
46
167
|
{
|
|
47
|
-
title:
|
|
168
|
+
title: "Adding the uploads SDL and service...",
|
|
48
169
|
task: async () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"signedUrl.ts.template"
|
|
170
|
+
await writeTemplateFile(
|
|
171
|
+
"uploads.sdl.ts.template",
|
|
172
|
+
path.join(paths.api.graphql, `uploads.sdl.${ext}`)
|
|
53
173
|
);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
58
|
-
const uploadsPath = path.join(
|
|
59
|
-
getPaths().api.functions,
|
|
60
|
-
`signedUrl.${projectIsTypescript ? "ts" : "js"}`
|
|
174
|
+
await writeTemplateFile(
|
|
175
|
+
"uploads.ts.template",
|
|
176
|
+
path.join(paths.api.services, "uploads", `uploads.${ext}`)
|
|
61
177
|
);
|
|
62
|
-
const uploadsContent = projectIsTypescript ? templateContent : await transformTSToJS(uploadsPath, templateContent);
|
|
63
|
-
return writeFile(uploadsPath, uploadsContent, {
|
|
64
|
-
overwriteExisting: force
|
|
65
|
-
});
|
|
66
178
|
}
|
|
67
179
|
},
|
|
68
180
|
{
|
|
69
|
-
...
|
|
70
|
-
|
|
181
|
+
title: "Adding the upload directives...",
|
|
182
|
+
task: async () => {
|
|
183
|
+
const directives = ["requireUploadToken", "withSignedUrl"];
|
|
184
|
+
if (wantsDb) {
|
|
185
|
+
directives.push("withDataUri");
|
|
186
|
+
}
|
|
187
|
+
for (const name of directives) {
|
|
188
|
+
await writeTemplateFile(
|
|
189
|
+
`${name}.ts.template`,
|
|
190
|
+
path.join(paths.api.directives, name, `${name}.${ext}`)
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
71
194
|
},
|
|
72
195
|
{
|
|
73
|
-
title: "
|
|
74
|
-
task:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
196
|
+
title: "Adding the server file...",
|
|
197
|
+
task: (_ctx, task) => task.newListr(setupServerFileTasks({ force: false })),
|
|
198
|
+
skip: () => fs.existsSync(serverFilePath) ? "api/src/server already exists; skipping." : false
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
title: "Registering the upload plugin in api/src/server...",
|
|
202
|
+
task: async (_ctx, task) => {
|
|
203
|
+
const source = fs.readFileSync(serverFilePath, "utf-8");
|
|
204
|
+
if (hasUploadsPlugin(source)) {
|
|
205
|
+
task.skip("The upload plugin is already registered; skipping.");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const graphqlPath = path.join(paths.api.functions, `graphql.${ext}`);
|
|
209
|
+
const authPath = path.join(paths.api.lib, `auth.${ext}`);
|
|
210
|
+
const auth = fs.existsSync(graphqlPath) && fs.existsSync(authPath) && fs.readFileSync(authPath, "utf-8").includes("getCurrentUser") ? detectServerAuth(fs.readFileSync(graphqlPath, "utf-8")) : null;
|
|
211
|
+
let updated;
|
|
212
|
+
try {
|
|
213
|
+
updated = addUploadsPlugin(source, { auth });
|
|
214
|
+
} catch {
|
|
89
215
|
throw new Error(
|
|
90
|
-
|
|
216
|
+
`Could not find \`await server.start()\` in ${path.relative(
|
|
217
|
+
paths.base,
|
|
218
|
+
serverFilePath
|
|
219
|
+
)}. Register the plugin by hand before the server starts:
|
|
220
|
+
|
|
221
|
+
import { cedarUploadsPlugin } from '@cedarjs/uploads'
|
|
222
|
+
import { db } from 'src/lib/db'
|
|
223
|
+
import { targets } from 'src/lib/uploads'
|
|
224
|
+
|
|
225
|
+
await server.register(cedarUploadsPlugin, {
|
|
226
|
+
tokenSecret: process.env.UPLOAD_TOKEN_SECRET,
|
|
227
|
+
targets,
|
|
228
|
+
db,
|
|
229
|
+
})
|
|
230
|
+
`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
fs.writeFileSync(serverFilePath, updated);
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
title: "Adding environment variables to .env...",
|
|
238
|
+
task: () => {
|
|
239
|
+
addEnvVar(
|
|
240
|
+
"UPLOAD_TOKEN_SECRET",
|
|
241
|
+
generateSecret(),
|
|
242
|
+
"Signs upload tokens and file-serving URLs. Keep it secret and use a different value per environment."
|
|
243
|
+
);
|
|
244
|
+
if (wantsFs) {
|
|
245
|
+
addEnvVar(
|
|
246
|
+
"UPLOAD_SERVE_BASE_URL",
|
|
247
|
+
"http://localhost:8911",
|
|
248
|
+
"Origin the api server is reachable at, used to build signed URLs for locally stored files."
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (wantsS3) {
|
|
252
|
+
addEnvVar(
|
|
253
|
+
"AWS_REGION",
|
|
254
|
+
"us-east-1",
|
|
255
|
+
"Region of the uploads bucket."
|
|
256
|
+
);
|
|
257
|
+
addEnvVar("AWS_ACCESS_KEY_ID", "replace-me", "");
|
|
258
|
+
addEnvVar("AWS_SECRET_ACCESS_KEY", "replace-me", "");
|
|
259
|
+
addEnvVar(
|
|
260
|
+
"S3_BUCKET_UPLOADS",
|
|
261
|
+
"replace-me",
|
|
262
|
+
"Bucket direct uploads land in."
|
|
91
263
|
);
|
|
92
264
|
}
|
|
93
265
|
}
|
|
@@ -96,10 +268,10 @@ const handler = async ({ force }) => {
|
|
|
96
268
|
title: "Prettifying changed files",
|
|
97
269
|
task: async (_ctx, task) => {
|
|
98
270
|
const prettifyPaths = [
|
|
99
|
-
|
|
100
|
-
path.join(
|
|
101
|
-
path.join(
|
|
102
|
-
path.join(
|
|
271
|
+
serverFilePath,
|
|
272
|
+
path.join(paths.api.lib, `uploads.${ext}`),
|
|
273
|
+
path.join(paths.api.graphql, `uploads.sdl.${ext}`),
|
|
274
|
+
path.join(paths.api.services, "uploads", `uploads.${ext}`)
|
|
103
275
|
];
|
|
104
276
|
for (const prettifyPath of prettifyPaths) {
|
|
105
277
|
try {
|
|
@@ -108,11 +280,11 @@ const handler = async ({ force }) => {
|
|
|
108
280
|
}
|
|
109
281
|
const source = fs.readFileSync(prettifyPath, "utf-8");
|
|
110
282
|
const prettierOptions = await getPrettierOptions();
|
|
111
|
-
const
|
|
283
|
+
const prettified = await format(source, {
|
|
112
284
|
...prettierOptions,
|
|
113
285
|
parser: "babel-ts"
|
|
114
286
|
});
|
|
115
|
-
fs.writeFileSync(prettifyPath,
|
|
287
|
+
fs.writeFileSync(prettifyPath, prettified, "utf-8");
|
|
116
288
|
} catch {
|
|
117
289
|
task.output = "Couldn't prettify the changes. Please reformat the files manually if needed.";
|
|
118
290
|
}
|
|
@@ -122,15 +294,30 @@ const handler = async ({ force }) => {
|
|
|
122
294
|
{
|
|
123
295
|
title: "One more thing...",
|
|
124
296
|
task: (_ctx, task) => {
|
|
297
|
+
const migrate = c.highlight(
|
|
298
|
+
formatCedarCommand(["prisma", "migrate", "dev"])
|
|
299
|
+
);
|
|
300
|
+
const types = c.highlight(formatCedarCommand(["generate", "types"]));
|
|
125
301
|
task.title = `One more thing...
|
|
126
302
|
|
|
127
|
-
${c.success("
|
|
303
|
+
${c.success("Uploads configured!")}
|
|
304
|
+
|
|
305
|
+
Next steps:
|
|
306
|
+
|
|
307
|
+
1. Apply the schema change: ${migrate}
|
|
308
|
+
2. Regenerate types: ${types}
|
|
309
|
+
3. Review the storage targets and upload profiles in api/src/lib/uploads.${ext}${wantsS3 ? "\n 4. Fill in the AWS_* and S3_BUCKET_UPLOADS values in .env" : ""}
|
|
128
310
|
|
|
129
|
-
|
|
311
|
+
Upload tokens are bound to the logged-in user when the app has auth.
|
|
312
|
+
If you add auth later, pass
|
|
313
|
+
\`authenticate: createUploadAuthenticator({ authDecoder, getCurrentUser })\`
|
|
314
|
+
to the plugin in api/src/server.${ext}.
|
|
315
|
+
|
|
316
|
+
Schedule cleanupStaleUploads() from a recurring job to sweep uploads
|
|
317
|
+
that never completed.
|
|
130
318
|
|
|
131
319
|
Check out the docs for more info:
|
|
132
320
|
${terminalLink("", "https://cedarjs.com/docs/uploads")}
|
|
133
|
-
|
|
134
321
|
`;
|
|
135
322
|
}
|
|
136
323
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -16,11 +16,12 @@ import * as setupNeon from "./setup/neon/neon.js";
|
|
|
16
16
|
import * as setupPackage from "./setup/package/package.js";
|
|
17
17
|
import * as setupRealtime from "./setup/realtime/realtime.js";
|
|
18
18
|
import * as setupServerFile from "./setup/server-file/serverFile.js";
|
|
19
|
+
import * as setupTenancy from "./setup/tenancy/tenancy.js";
|
|
19
20
|
import * as setupUi from "./setup/ui/ui.js";
|
|
20
21
|
import * as setupUploads from "./setup/uploads/uploads.js";
|
|
21
22
|
const command = "setup <command>";
|
|
22
23
|
const description = "Initialize project config and install packages";
|
|
23
|
-
const builder = (yargs) => yargs.command(setupAuth).command(setupCache).command(setupDatabase).command(setupDeploy).command(setupDocker).command(setupGenerator).command(setupGraphql).command(setupI18n).command(setupJobs).command(setupMailer).command(setupMiddleware).command(setupMonitoring).command(setupNeon).command(setupPackage).command(setupRealtime).command(setupServerFile).command(setupUi).command(setupUploads).demandCommand().middleware(detectCedarVersion).epilogue(
|
|
24
|
+
const builder = (yargs) => yargs.command(setupAuth).command(setupCache).command(setupDatabase).command(setupDeploy).command(setupDocker).command(setupGenerator).command(setupGraphql).command(setupI18n).command(setupJobs).command(setupMailer).command(setupMiddleware).command(setupMonitoring).command(setupNeon).command(setupPackage).command(setupRealtime).command(setupServerFile).command(setupTenancy).command(setupUi).command(setupUploads).demandCommand().middleware(detectCedarVersion).epilogue(
|
|
24
25
|
`Also see the ${terminalLink(
|
|
25
26
|
"CedarJS CLI Reference",
|
|
26
27
|
"https://cedarjs.com/docs/cli-commands#setup"
|
package/dist/commands/studio.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import execa from "execa";
|
|
4
|
-
import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
5
1
|
import { runBin } from "@cedarjs/cli-helpers/packageManager/exec";
|
|
2
|
+
import { recordTelemetryAttributes } from "@cedarjs/cli-helpers/telemetry";
|
|
6
3
|
import { ensurePosixPath } from "@cedarjs/project-config";
|
|
7
4
|
import { errorTelemetry, timedTelemetry } from "@cedarjs/telemetry";
|
|
8
5
|
import { getPaths } from "../../lib/index.js";
|
|
9
6
|
import * as project from "../../lib/project.js";
|
|
10
7
|
import { warnIfNonStandardDatasourceUrl } from "./datasourceWarning.js";
|
|
8
|
+
import { assertSingleViteVersion } from "./viteVersionCheck.js";
|
|
11
9
|
function hasStringMessage(value) {
|
|
12
10
|
return typeof value === "object" && value !== null && "message" in value && value.message === "string";
|
|
13
11
|
}
|
|
@@ -17,73 +15,29 @@ function getExitCode(value) {
|
|
|
17
15
|
}
|
|
18
16
|
return value.exitCode;
|
|
19
17
|
}
|
|
20
|
-
function isInGitRepository() {
|
|
21
|
-
try {
|
|
22
|
-
execa.commandSync("git rev-parse --is-inside-work-tree");
|
|
23
|
-
return true;
|
|
24
|
-
} catch {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function isInMercurialRepository() {
|
|
29
|
-
try {
|
|
30
|
-
execa.commandSync("hg --cwd . root");
|
|
31
|
-
return true;
|
|
32
|
-
} catch {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function isJestConfigFile(sides) {
|
|
37
|
-
for (const side of sides) {
|
|
38
|
-
try {
|
|
39
|
-
const jestConfigExists = fs.existsSync(path.join(side, "jest.config.js")) || fs.existsSync(path.join(side, "jest.config.ts"));
|
|
40
|
-
if (!jestConfigExists) {
|
|
41
|
-
console.error(
|
|
42
|
-
c.error(
|
|
43
|
-
`
|
|
44
|
-
Error: Missing Jest config file ${side}/jest.config.js
|
|
45
|
-
To add this file, run \`npx @cedarjs/codemods update-jest-config\`
|
|
46
|
-
`
|
|
47
|
-
)
|
|
48
|
-
);
|
|
49
|
-
throw new Error(`Error: Jest config file not found in ${side} side`);
|
|
50
|
-
}
|
|
51
|
-
} catch (error) {
|
|
52
|
-
const message = hasStringMessage(error) ? error.message : `Error: Jest config file not found in ${side} side`;
|
|
53
|
-
errorTelemetry(process.argv, message);
|
|
54
|
-
process.exit(getExitCode(error) ?? 1);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
18
|
const handler = async ({
|
|
59
19
|
filter: filterParams = [],
|
|
60
|
-
watch = true,
|
|
61
|
-
collectCoverage = false,
|
|
62
20
|
dbPush = true,
|
|
63
21
|
force = false,
|
|
64
22
|
...others
|
|
65
23
|
}) => {
|
|
66
24
|
recordTelemetryAttributes({
|
|
67
25
|
command: "test",
|
|
68
|
-
watch,
|
|
69
|
-
collectCoverage,
|
|
70
26
|
dbPush
|
|
71
27
|
});
|
|
28
|
+
let watch = true;
|
|
72
29
|
const rwjsPaths = getPaths();
|
|
73
|
-
const
|
|
74
|
-
if ([
|
|
75
|
-
"collect-coverage",
|
|
76
|
-
"db-push",
|
|
77
|
-
"force",
|
|
78
|
-
"loadEnvFiles",
|
|
79
|
-
"watch",
|
|
80
|
-
"$0",
|
|
81
|
-
"_"
|
|
82
|
-
].includes(flagName)) {
|
|
30
|
+
const forwardVitestFlags = Object.keys(others).flatMap((flagName) => {
|
|
31
|
+
if (["db-push", "force", "loadEnvFiles", "$0", "_"].includes(flagName)) {
|
|
83
32
|
return [];
|
|
84
33
|
}
|
|
85
34
|
const flag = flagName.length > 1 ? `--${flagName}` : `-${flagName}`;
|
|
86
35
|
const flagValue = others[flagName];
|
|
36
|
+
if (flagName === "watch") {
|
|
37
|
+
watch = flagValue === true;
|
|
38
|
+
} else if (flagName === "run" && flagValue) {
|
|
39
|
+
watch = false;
|
|
40
|
+
}
|
|
87
41
|
if (Array.isArray(flagValue)) {
|
|
88
42
|
return flagValue.flatMap((val) => [flag, val]);
|
|
89
43
|
}
|
|
@@ -92,28 +46,25 @@ const handler = async ({
|
|
|
92
46
|
const sides = filterParams.filter(
|
|
93
47
|
(filterString) => project.workspaces().includes(filterString)
|
|
94
48
|
);
|
|
95
|
-
const
|
|
49
|
+
const vitestFilterArgs = [
|
|
96
50
|
...filterParams.filter(
|
|
97
51
|
(filterString) => !project.workspaces().includes(filterString)
|
|
98
52
|
)
|
|
99
53
|
];
|
|
100
|
-
const
|
|
101
|
-
...
|
|
102
|
-
...
|
|
103
|
-
collectCoverage ? "--collectCoverage" : null,
|
|
54
|
+
const vitestArgs = [
|
|
55
|
+
...vitestFilterArgs,
|
|
56
|
+
...forwardVitestFlags,
|
|
104
57
|
"--passWithNoTests"
|
|
105
58
|
].filter((flagOrValue) => flagOrValue !== null);
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
jestArgs.push(hasSourceControl ? "--watch" : "--watchAll");
|
|
59
|
+
if (process.env.CI) {
|
|
60
|
+
vitestArgs.push("--run");
|
|
109
61
|
}
|
|
110
|
-
if (!
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
62
|
+
if (!others["config"]) {
|
|
63
|
+
sides.forEach((side) => vitestArgs.push("--project", side));
|
|
64
|
+
if (!sides.length) {
|
|
65
|
+
project.workspaces().forEach((side) => sides.push(side));
|
|
66
|
+
}
|
|
115
67
|
}
|
|
116
|
-
isJestConfigFile(sides);
|
|
117
68
|
try {
|
|
118
69
|
const cacheDirDb = `file:${ensurePosixPath(
|
|
119
70
|
rwjsPaths.generated.base
|
|
@@ -126,8 +77,11 @@ const handler = async ({
|
|
|
126
77
|
if (sides.includes("api")) {
|
|
127
78
|
await warnIfNonStandardDatasourceUrl({ force });
|
|
128
79
|
}
|
|
80
|
+
if (sides.includes("web")) {
|
|
81
|
+
assertSingleViteVersion();
|
|
82
|
+
}
|
|
129
83
|
const runCommand = async () => {
|
|
130
|
-
await runBin("
|
|
84
|
+
await runBin("vitest", vitestArgs, {
|
|
131
85
|
cwd: rwjsPaths.base,
|
|
132
86
|
stdio: "inherit",
|
|
133
87
|
env: {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { getPaths } from "@cedarjs/project-config";
|
|
5
|
+
function resolvePackage(fromFile, packageName) {
|
|
6
|
+
try {
|
|
7
|
+
const require2 = createRequire(fromFile);
|
|
8
|
+
const packageJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
9
|
+
const packageJson = JSON.parse(
|
|
10
|
+
fs.readFileSync(packageJsonPath, "utf-8")
|
|
11
|
+
);
|
|
12
|
+
if (typeof packageJson !== "object" || packageJson === null || !("version" in packageJson) || typeof packageJson.version !== "string") {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
version: packageJson.version,
|
|
17
|
+
dir: path.dirname(packageJsonPath)
|
|
18
|
+
};
|
|
19
|
+
} catch {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resolveViteVersions({
|
|
24
|
+
projectBase,
|
|
25
|
+
webBase
|
|
26
|
+
}) {
|
|
27
|
+
const vitest = resolvePackage(
|
|
28
|
+
path.join(projectBase, "package.json"),
|
|
29
|
+
"vitest"
|
|
30
|
+
);
|
|
31
|
+
const vitestVite = vitest ? resolvePackage(path.join(vitest.dir, "package.json"), "vite") : void 0;
|
|
32
|
+
const cedarVite = resolvePackage(
|
|
33
|
+
path.join(webBase, "package.json"),
|
|
34
|
+
"@cedarjs/vite"
|
|
35
|
+
);
|
|
36
|
+
const frameworkVite = cedarVite ? resolvePackage(path.join(cedarVite.dir, "package.json"), "vite") : void 0;
|
|
37
|
+
return { vitestVite, frameworkVite };
|
|
38
|
+
}
|
|
39
|
+
function getViteVersionMismatchMessage({
|
|
40
|
+
vitestVite,
|
|
41
|
+
frameworkVite
|
|
42
|
+
}) {
|
|
43
|
+
if (!vitestVite || !frameworkVite) {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
if (vitestVite.version === frameworkVite.version) {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
return `Vitest is using Vite ${vitestVite.version} from
|
|
50
|
+
${vitestVite.dir}
|
|
51
|
+
but CedarJS is built against Vite ${frameworkVite.version} from
|
|
52
|
+
${frameworkVite.dir}
|
|
53
|
+
|
|
54
|
+
The CedarJS Vite plugins only work with the Vite version they're built against, so web tests can't run while Vitest uses a different copy. This happens when the project doesn't pin \`vite\`, because Vitest accepts a wide range of Vite versions and installs the newest one.
|
|
55
|
+
|
|
56
|
+
Please add an override/resolution that pins vite to ${frameworkVite.version}, then reinstall your dependencies.`;
|
|
57
|
+
}
|
|
58
|
+
function assertSingleViteVersion() {
|
|
59
|
+
const cedarPaths = getPaths();
|
|
60
|
+
const message = getViteVersionMismatchMessage(
|
|
61
|
+
resolveViteVersions({
|
|
62
|
+
projectBase: cedarPaths.base,
|
|
63
|
+
webBase: cedarPaths.web.base
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
if (message) {
|
|
67
|
+
console.error(message);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
assertSingleViteVersion,
|
|
73
|
+
getViteVersionMismatchMessage,
|
|
74
|
+
resolveViteVersions
|
|
75
|
+
};
|