@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
package/dist/commands/lint.js
CHANGED
|
@@ -1,8 +1,63 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { terminalLink } from "termi-link";
|
|
3
4
|
import { recordTelemetryAttributes } from "@cedarjs/cli-helpers";
|
|
4
5
|
import { runBin } from "@cedarjs/cli-helpers/packageManager/exec";
|
|
5
|
-
import { getPaths } from "@cedarjs/project-config";
|
|
6
|
+
import { getPaths, getConfig } from "@cedarjs/project-config";
|
|
7
|
+
function detectLegacyEslintConfig() {
|
|
8
|
+
const projectRoot = getPaths().base;
|
|
9
|
+
const legacyConfigFiles = [
|
|
10
|
+
".eslintrc.js",
|
|
11
|
+
".eslintrc.cjs",
|
|
12
|
+
".eslintrc.json",
|
|
13
|
+
".eslintrc.yaml",
|
|
14
|
+
".eslintrc.yml"
|
|
15
|
+
];
|
|
16
|
+
const foundLegacyFiles = [];
|
|
17
|
+
for (const configFile of legacyConfigFiles) {
|
|
18
|
+
if (fs.existsSync(path.join(projectRoot, configFile))) {
|
|
19
|
+
foundLegacyFiles.push(configFile);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
23
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
24
|
+
try {
|
|
25
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
26
|
+
if (packageJson.eslintConfig) {
|
|
27
|
+
foundLegacyFiles.push("package.json (eslintConfig field)");
|
|
28
|
+
}
|
|
29
|
+
if (packageJson.eslint) {
|
|
30
|
+
foundLegacyFiles.push("package.json (eslint field)");
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return foundLegacyFiles;
|
|
36
|
+
}
|
|
37
|
+
function showLegacyEslintDeprecationWarning(legacyFiles) {
|
|
38
|
+
console.warn("");
|
|
39
|
+
console.warn("\u26A0\uFE0F DEPRECATION WARNING: Legacy ESLint Configuration Detected");
|
|
40
|
+
console.warn("");
|
|
41
|
+
console.warn(" The following legacy ESLint configuration files were found:");
|
|
42
|
+
legacyFiles.forEach((file) => {
|
|
43
|
+
console.warn(` - ${file}`);
|
|
44
|
+
});
|
|
45
|
+
console.warn("");
|
|
46
|
+
console.warn(
|
|
47
|
+
" Cedar has migrated to ESLint flat config format. Legacy configurations"
|
|
48
|
+
);
|
|
49
|
+
console.warn(
|
|
50
|
+
" still work but are deprecated and will be removed in a future version."
|
|
51
|
+
);
|
|
52
|
+
console.warn("");
|
|
53
|
+
console.warn(" To migrate to the new format:");
|
|
54
|
+
console.warn(" 1. Remove the legacy config file(s) listed above");
|
|
55
|
+
console.warn(" 2. Create an eslint.config.mjs");
|
|
56
|
+
console.warn(" 3. Use the flat config format with @cedarjs/eslint-config");
|
|
57
|
+
console.warn("");
|
|
58
|
+
console.warn(" See more here: https://github.com/cedarjs/cedar/pull/629");
|
|
59
|
+
console.warn("");
|
|
60
|
+
}
|
|
6
61
|
const command = "lint [paths..]";
|
|
7
62
|
const description = "Lint your files";
|
|
8
63
|
const builder = (yargs) => {
|
|
@@ -31,6 +86,11 @@ const handler = async ({
|
|
|
31
86
|
format = "stylish"
|
|
32
87
|
}) => {
|
|
33
88
|
recordTelemetryAttributes({ command: "lint", fix, format });
|
|
89
|
+
const config = getConfig();
|
|
90
|
+
const legacyConfigFiles = detectLegacyEslintConfig();
|
|
91
|
+
if (legacyConfigFiles.length > 0 && config instanceof Object && "eslintLegacyConfigWarning" in config && config.eslintLegacyConfigWarning) {
|
|
92
|
+
showLegacyEslintDeprecationWarning(legacyConfigFiles);
|
|
93
|
+
}
|
|
34
94
|
try {
|
|
35
95
|
const sbPath = getPaths().web.storybook;
|
|
36
96
|
const eslintArgs = [
|
|
@@ -3,8 +3,7 @@ import boxen from "boxen";
|
|
|
3
3
|
import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
4
4
|
import {
|
|
5
5
|
formatCedarCommand,
|
|
6
|
-
formatRunBinCommand
|
|
7
|
-
formatRunTransitiveBinCommand
|
|
6
|
+
formatRunBinCommand
|
|
8
7
|
} from "@cedarjs/cli-helpers/packageManager/display";
|
|
9
8
|
import { runTransitiveBinSync } from "@cedarjs/cli-helpers/packageManager/exec";
|
|
10
9
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
@@ -50,14 +49,18 @@ const handler = async ({
|
|
|
50
49
|
for (const [name, value] of Object.entries(options)) {
|
|
51
50
|
args.push(name.length > 1 ? `--${name}` : `-${name}`);
|
|
52
51
|
if (typeof value === "string") {
|
|
53
|
-
|
|
52
|
+
if (value.split(" ").length > 1) {
|
|
53
|
+
args.push(`"${value}"`);
|
|
54
|
+
} else {
|
|
55
|
+
args.push(value);
|
|
56
|
+
}
|
|
54
57
|
} else if (typeof value === "number") {
|
|
55
58
|
args.push(String(value));
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
console.log();
|
|
59
62
|
console.log(c.note("Running Prisma CLI..."));
|
|
60
|
-
console.log(c.underline(`$ ${
|
|
63
|
+
console.log(c.underline(`$ <pm exec> prisma ${args.join(" ")}`));
|
|
61
64
|
console.log();
|
|
62
65
|
try {
|
|
63
66
|
runTransitiveBinSync("prisma", args, {
|
package/dist/commands/serve.js
CHANGED
|
@@ -6,18 +6,11 @@ import { terminalLink } from "termi-link";
|
|
|
6
6
|
import * as apiServerCLIConfig from "@cedarjs/api-server/apiCliConfig";
|
|
7
7
|
import * as bothServerCLIConfig from "@cedarjs/api-server/bothCliConfig";
|
|
8
8
|
import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
9
|
+
import { projectIsEsm } from "@cedarjs/project-config";
|
|
9
10
|
import * as webServerCLIConfig from "@cedarjs/web-server";
|
|
10
11
|
import { getPaths, getConfig } from "../lib/index.js";
|
|
11
12
|
import { serverFileExists } from "../lib/project.js";
|
|
12
13
|
import { webSsrServerHandler } from "./serveWebHandler.js";
|
|
13
|
-
function refuseServerFileUnderUD() {
|
|
14
|
-
console.error(
|
|
15
|
-
c.error(
|
|
16
|
-
"\n api/src/server.ts was detected, but a custom server file is not supported with --ud. It is a Fastify concept \u2014 anything registered there (Realtime, custom plugins, custom middleware) would silently be skipped if serving continued.\n"
|
|
17
|
-
)
|
|
18
|
-
);
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
14
|
function resolveUDEntryPath() {
|
|
22
15
|
const base = path.join(getPaths().api.dist, "ud", "index");
|
|
23
16
|
for (const ext of [".mjs", ".js"]) {
|
|
@@ -98,16 +91,20 @@ const builder = async (yargs) => {
|
|
|
98
91
|
process.exit(1);
|
|
99
92
|
}
|
|
100
93
|
if (serverFileExists()) {
|
|
101
|
-
|
|
94
|
+
console.warn(
|
|
95
|
+
c.warning(
|
|
96
|
+
"\n Note: api/src/server.ts was detected. This file is a Fastify concept and will be ignored when using --ud. You are testing the experimental UD support, so the behavior will not match your production Fastify setup.\n"
|
|
97
|
+
)
|
|
98
|
+
);
|
|
102
99
|
}
|
|
103
100
|
const { getAPIHost, getAPIPort, getWebHost, getWebPort } = await import("@cedarjs/api-server/cliHelpers");
|
|
104
101
|
const apiPort = argv.apiPort ?? getAPIPort();
|
|
105
102
|
const apiHost = argv.apiHost ?? getAPIHost();
|
|
106
|
-
const webPort = argv.webPort ?? getWebPort(
|
|
107
|
-
const webHost = argv.webHost ?? getWebHost(
|
|
103
|
+
const webPort = argv.webPort ?? getWebPort();
|
|
104
|
+
const webHost = argv.webHost ?? getWebHost();
|
|
108
105
|
const apiRootPath = argv.apiRootPath ?? "/";
|
|
109
106
|
const apiTarget = `http://${apiHost.includes(":") ? `[${apiHost}]` : apiHost}:${apiPort}`;
|
|
110
|
-
const {
|
|
107
|
+
const { serveStatic } = await import("srvx/static");
|
|
111
108
|
const apiUrl = getConfig().web.apiUrl;
|
|
112
109
|
const webDist = getPaths().web.dist;
|
|
113
110
|
const prerenderIndexPath = path.join(webDist, "200.html");
|
|
@@ -117,7 +114,7 @@ const builder = async (yargs) => {
|
|
|
117
114
|
// Dummy fetch handler. All requests are handled by middleware
|
|
118
115
|
fetch: async () => new Response("Not Found", { status: 404 }),
|
|
119
116
|
middleware: [
|
|
120
|
-
|
|
117
|
+
serveStatic({ dir: webDist }),
|
|
121
118
|
async (req, next) => {
|
|
122
119
|
const url = new URL(req.url, "http://localhost");
|
|
123
120
|
if (!url.pathname.startsWith(apiUrl)) {
|
|
@@ -165,7 +162,12 @@ const builder = async (yargs) => {
|
|
|
165
162
|
const serveBothHandlers = await import("./serveBothHandler.js");
|
|
166
163
|
await serveBothHandlers.bothSsrRscServerHandler(argv, rscEnabled);
|
|
167
164
|
} else {
|
|
168
|
-
|
|
165
|
+
if (!projectIsEsm()) {
|
|
166
|
+
const { handler } = await import("@cedarjs/api-server/cjs/bothCliConfigHandler");
|
|
167
|
+
await handler(argv);
|
|
168
|
+
} else {
|
|
169
|
+
await bothServerCLIConfig.handler(argv);
|
|
170
|
+
}
|
|
169
171
|
}
|
|
170
172
|
}
|
|
171
173
|
}).command({
|
|
@@ -191,15 +193,7 @@ const builder = async (yargs) => {
|
|
|
191
193
|
socket: argv.socket,
|
|
192
194
|
apiRootPath: argv.apiRootPath
|
|
193
195
|
});
|
|
194
|
-
const { getAPIHost, getAPIPort } = await import("@cedarjs/api-server/cliHelpers");
|
|
195
|
-
const apiPort = argv.port ?? getAPIPort({ isPublicSide: true });
|
|
196
|
-
const apiHost = argv.host ?? getAPIHost({ isPublicSide: true });
|
|
197
|
-
argv.port = apiPort;
|
|
198
|
-
argv.host = apiHost;
|
|
199
196
|
if (argv.ud) {
|
|
200
|
-
if (serverFileExists()) {
|
|
201
|
-
refuseServerFileUnderUD();
|
|
202
|
-
}
|
|
203
197
|
const udEntryPath = resolveUDEntryPath();
|
|
204
198
|
if (!udEntryPath) {
|
|
205
199
|
console.error(
|
|
@@ -209,6 +203,8 @@ const builder = async (yargs) => {
|
|
|
209
203
|
);
|
|
210
204
|
process.exit(1);
|
|
211
205
|
}
|
|
206
|
+
const apiPort = argv.port ?? parseInt(process.env.PORT ?? "8911", 10);
|
|
207
|
+
const apiHost = argv.host ?? process.env.HOST ?? "localhost";
|
|
212
208
|
process.stdout.write(
|
|
213
209
|
`API server starting at http://${apiHost}:${apiPort}...`
|
|
214
210
|
);
|
|
@@ -223,7 +219,12 @@ const builder = async (yargs) => {
|
|
|
223
219
|
const { apiServerFileHandler } = await import("./serveApiHandler.js");
|
|
224
220
|
await apiServerFileHandler(argv);
|
|
225
221
|
} else {
|
|
226
|
-
|
|
222
|
+
if (!projectIsEsm()) {
|
|
223
|
+
const { handler } = await import("@cedarjs/api-server/cjs/apiCliConfigHandler");
|
|
224
|
+
await handler(argv);
|
|
225
|
+
} else {
|
|
226
|
+
await apiServerCLIConfig.handler(argv);
|
|
227
|
+
}
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
}).command({
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import concurrently from "concurrently";
|
|
3
|
-
import { handler as apiServerHandler } from "@cedarjs/api-server/apiCliConfigHandler";
|
|
3
|
+
import { handler as apiServerHandler } from "@cedarjs/api-server/cjs/apiCliConfigHandler";
|
|
4
4
|
import {
|
|
5
5
|
getAPIHost,
|
|
6
6
|
getAPIPort,
|
|
7
7
|
getAPIRootPath,
|
|
8
8
|
getWebHost,
|
|
9
9
|
getWebPort
|
|
10
|
-
} from "@cedarjs/api-server/cliHelpers";
|
|
10
|
+
} from "@cedarjs/api-server/cjs/cliHelpers";
|
|
11
11
|
import { formatRunBinCommand } from "@cedarjs/cli-helpers/packageManager/display";
|
|
12
12
|
import { runBin } from "@cedarjs/cli-helpers/packageManager/exec";
|
|
13
13
|
import { getConfig, getPaths } from "@cedarjs/project-config";
|
|
@@ -27,8 +27,8 @@ const bothServerFileHandler = async (argv) => {
|
|
|
27
27
|
} else {
|
|
28
28
|
argv.apiPort ??= getAPIPort();
|
|
29
29
|
argv.apiHost ??= getAPIHost();
|
|
30
|
-
argv.webPort ??= getWebPort(
|
|
31
|
-
argv.webHost ??= getWebHost(
|
|
30
|
+
argv.webPort ??= getWebPort();
|
|
31
|
+
argv.webHost ??= getWebHost();
|
|
32
32
|
const apiRootPath = argv.apiRootPath ?? getAPIRootPath();
|
|
33
33
|
const apiProxyTarget = [
|
|
34
34
|
"http://",
|
|
@@ -237,7 +237,7 @@ async function getAuthSetupHandler(module) {
|
|
|
237
237
|
});
|
|
238
238
|
}
|
|
239
239
|
const setupModule = await import(module);
|
|
240
|
-
return setupModule.handler;
|
|
240
|
+
return setupModule.default.handler;
|
|
241
241
|
}
|
|
242
242
|
function isInstalled(module) {
|
|
243
243
|
const { dependencies, devDependencies } = JSON.parse(
|
|
@@ -4,6 +4,7 @@ import * as parser from "@babel/parser";
|
|
|
4
4
|
import * as t from "@babel/types";
|
|
5
5
|
import execa from "execa";
|
|
6
6
|
import { Listr } from "listr2";
|
|
7
|
+
import * as recast from "recast";
|
|
7
8
|
import { getConfigPath, getConfig } from "@cedarjs/project-config";
|
|
8
9
|
import { getPaths, writeFilesTask } from "../../../../lib/index.js";
|
|
9
10
|
const updateApiURLTask = (apiUrl) => {
|
|
@@ -87,6 +88,14 @@ const verifyUDSetupTask = () => {
|
|
|
87
88
|
}
|
|
88
89
|
};
|
|
89
90
|
};
|
|
91
|
+
function posToIndex(str, line, column) {
|
|
92
|
+
const lines = str.split("\n");
|
|
93
|
+
let index = 0;
|
|
94
|
+
for (let i = 0; i < line - 1; i++) {
|
|
95
|
+
index += lines[i].length + 1;
|
|
96
|
+
}
|
|
97
|
+
return index + column;
|
|
98
|
+
}
|
|
90
99
|
function resolveConfigObject(arg) {
|
|
91
100
|
if (t.isObjectExpression(arg)) {
|
|
92
101
|
return arg;
|
|
@@ -118,9 +127,15 @@ function insertPluginsBeforeCedar({
|
|
|
118
127
|
content,
|
|
119
128
|
pluginCodes
|
|
120
129
|
}) {
|
|
121
|
-
const ast =
|
|
122
|
-
|
|
123
|
-
|
|
130
|
+
const ast = recast.parse(content, {
|
|
131
|
+
parser: {
|
|
132
|
+
parse(source) {
|
|
133
|
+
return parser.parse(source, {
|
|
134
|
+
sourceType: "module",
|
|
135
|
+
plugins: ["typescript", "jsx"]
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
124
139
|
});
|
|
125
140
|
const defaultExport = ast.program.body.find(t.isExportDefaultDeclaration);
|
|
126
141
|
if (!defaultExport) {
|
|
@@ -148,18 +163,33 @@ function insertPluginsBeforeCedar({
|
|
|
148
163
|
return null;
|
|
149
164
|
}
|
|
150
165
|
const cedarNode = cedarElement;
|
|
151
|
-
if (!cedarNode.loc || !arrayExpr.loc || !pluginsProp.loc
|
|
166
|
+
if (!cedarNode.loc || !arrayExpr.loc || !pluginsProp.loc) {
|
|
152
167
|
return null;
|
|
153
168
|
}
|
|
154
169
|
const isInline = cedarNode.loc.start.line === arrayExpr.loc.start.line;
|
|
155
170
|
if (isInline) {
|
|
156
|
-
const
|
|
157
|
-
|
|
171
|
+
const startPos = posToIndex(
|
|
172
|
+
content,
|
|
173
|
+
arrayExpr.loc.start.line,
|
|
174
|
+
arrayExpr.loc.start.column
|
|
175
|
+
);
|
|
176
|
+
const endPos = posToIndex(
|
|
177
|
+
content,
|
|
178
|
+
arrayExpr.loc.end.line,
|
|
179
|
+
arrayExpr.loc.end.column
|
|
180
|
+
);
|
|
181
|
+
const precedingText = content.slice(0, startPos);
|
|
182
|
+
const followingText = content.slice(endPos);
|
|
158
183
|
const existingCodes = elements.flatMap((el) => {
|
|
159
|
-
if (el?.
|
|
184
|
+
if (!el?.loc) {
|
|
160
185
|
return [];
|
|
161
186
|
}
|
|
162
|
-
return [
|
|
187
|
+
return [
|
|
188
|
+
content.slice(
|
|
189
|
+
posToIndex(content, el.loc.start.line, el.loc.start.column),
|
|
190
|
+
posToIndex(content, el.loc.end.line, el.loc.end.column)
|
|
191
|
+
)
|
|
192
|
+
];
|
|
163
193
|
});
|
|
164
194
|
const lines2 = content.split("\n");
|
|
165
195
|
const pluginsLine = pluginsProp.loc.start.line;
|
|
@@ -175,7 +205,7 @@ function insertPluginsBeforeCedar({
|
|
|
175
205
|
return precedingText + multiline + followingText;
|
|
176
206
|
}
|
|
177
207
|
const cedarLine = cedarNode.loc.start.line;
|
|
178
|
-
const insertPos =
|
|
208
|
+
const insertPos = posToIndex(content, cedarLine, 0);
|
|
179
209
|
const lines = content.split("\n");
|
|
180
210
|
const indent = (lines[cedarLine - 1].match(/^\s*/) ?? [""])[0];
|
|
181
211
|
const insertion = pluginCodes.map((code) => `${indent}${code},
|
|
@@ -16,10 +16,6 @@ import {
|
|
|
16
16
|
mysqlDatabaseService
|
|
17
17
|
} from "../templates/flightcontrol.js";
|
|
18
18
|
const { getConfig } = prismaInternals;
|
|
19
|
-
const APOLLO_PROVIDER_COMPONENT_NAMES = [
|
|
20
|
-
"CedarApolloProvider",
|
|
21
|
-
"RedwoodApolloProvider"
|
|
22
|
-
];
|
|
23
19
|
const getFlightcontrolJson = async (database) => {
|
|
24
20
|
const flightcontrolConfig = getFlightcontrolConfig();
|
|
25
21
|
if (database === "none") {
|
|
@@ -209,26 +205,18 @@ const updateApp = () => {
|
|
|
209
205
|
appContent[authLineIndex] = ` <AuthProvider type="dbAuth" config={{ fetchConfig: { credentials: 'include' } }}>
|
|
210
206
|
`;
|
|
211
207
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
gqlLineIndex = appContent.findIndex(
|
|
216
|
-
(line) => line.includes(`<${componentName}`)
|
|
217
|
-
);
|
|
218
|
-
if (gqlLineIndex !== -1) {
|
|
219
|
-
apolloProviderComponentName = componentName;
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
208
|
+
const gqlLineIndex = appContent.findIndex(
|
|
209
|
+
(line) => line.includes("<RedwoodApolloProvider")
|
|
210
|
+
);
|
|
223
211
|
if (gqlLineIndex === -1) {
|
|
224
212
|
console.log(`
|
|
225
|
-
Couldn't find <
|
|
213
|
+
Couldn't find <RedwoodApolloProvider in web/src/App.js
|
|
226
214
|
If (and when) you use *dbAuth*, you'll have to add the following fetch config manually:
|
|
227
215
|
|
|
228
216
|
graphQLClientConfig={{ httpLinkConfig: { credentials: 'include' }}}
|
|
229
217
|
`);
|
|
230
218
|
} else if (appContent.toString().match(/dbAuth/)) {
|
|
231
|
-
appContent[gqlLineIndex] = `
|
|
219
|
+
appContent[gqlLineIndex] = ` <RedwoodApolloProvider graphQLClientConfig={{ httpLinkConfig: { credentials: 'include' }}} >
|
|
232
220
|
`;
|
|
233
221
|
}
|
|
234
222
|
fs.writeFileSync(appPath, appContent.join(EOL));
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import prismaInternals from "@prisma/internals";
|
|
3
3
|
import { Listr } from "listr2";
|
|
4
|
-
import prompts from "prompts";
|
|
5
4
|
import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
|
|
6
5
|
import { getPaths, getPrismaSchemas } from "@cedarjs/project-config";
|
|
7
6
|
import { errorTelemetry } from "@cedarjs/telemetry";
|
|
8
7
|
import { writeFilesTask, printSetupNotes } from "../../../../lib/index.js";
|
|
9
|
-
import {
|
|
8
|
+
import { addFilesTask } from "../helpers/index.js";
|
|
9
|
+
import {
|
|
10
|
+
POSTGRES_YAML,
|
|
11
|
+
RENDER_HEALTH_CHECK,
|
|
12
|
+
RENDER_YAML,
|
|
13
|
+
SQLITE_YAML
|
|
14
|
+
} from "../templates/render.js";
|
|
10
15
|
const { getConfig } = prismaInternals;
|
|
11
|
-
const SQLITE_API_PLAN = "starter";
|
|
12
16
|
const getRenderYamlContent = async (database) => {
|
|
13
17
|
if (database === "none") {
|
|
14
18
|
return {
|
|
@@ -29,7 +33,7 @@ const getRenderYamlContent = async (database) => {
|
|
|
29
33
|
case "sqlite":
|
|
30
34
|
return {
|
|
31
35
|
path: path.join(getPaths().base, "render.yaml"),
|
|
32
|
-
content: RENDER_YAML(SQLITE_YAML
|
|
36
|
+
content: RENDER_YAML(SQLITE_YAML)
|
|
33
37
|
};
|
|
34
38
|
default:
|
|
35
39
|
throw new Error(`
|
|
@@ -50,9 +54,14 @@ const getRenderYamlContent = async (database) => {
|
|
|
50
54
|
const notes = [
|
|
51
55
|
"You are ready to deploy to Render!\n",
|
|
52
56
|
"Go to https://dashboard.render.com/iacs to create your account and deploy to Render",
|
|
53
|
-
"Check out the deployment docs at https://
|
|
54
|
-
"Note: After first deployment to Render update the rewrite rule destination in `./render.yaml`"
|
|
55
|
-
|
|
57
|
+
"Check out the deployment docs at https://render.com/docs/deploy-redwood for detailed instructions",
|
|
58
|
+
"Note: After first deployment to Render update the rewrite rule destination in `./render.yaml`"
|
|
59
|
+
];
|
|
60
|
+
const additionalFiles = [
|
|
61
|
+
{
|
|
62
|
+
path: path.join(getPaths().base, "api/src/functions/healthz.js"),
|
|
63
|
+
content: RENDER_HEALTH_CHECK
|
|
64
|
+
}
|
|
56
65
|
];
|
|
57
66
|
const handler = async ({
|
|
58
67
|
force,
|
|
@@ -63,26 +72,6 @@ const handler = async ({
|
|
|
63
72
|
force,
|
|
64
73
|
database
|
|
65
74
|
});
|
|
66
|
-
if (database === "sqlite") {
|
|
67
|
-
console.warn(
|
|
68
|
-
c.warning(
|
|
69
|
-
`Render's free plan doesn't support persistent disks, which the \`sqlite\` deploy option requires for its database file. The generated render.yaml will set the api service's plan to "${SQLITE_API_PLAN}" (a paid plan) instead of "free" so the disk can actually attach.
|
|
70
|
-
|
|
71
|
-
If you want to stay on the free plan, rerun this command with \`--database postgresql\` (a managed database, not a disk) or \`--database none\`.`
|
|
72
|
-
)
|
|
73
|
-
);
|
|
74
|
-
console.log();
|
|
75
|
-
const { confirmed } = await prompts({
|
|
76
|
-
type: "confirm",
|
|
77
|
-
name: "confirmed",
|
|
78
|
-
message: `Generate render.yaml with the api service on the "${SQLITE_API_PLAN}" plan?`
|
|
79
|
-
});
|
|
80
|
-
if (!confirmed) {
|
|
81
|
-
console.log("Aborting render setup.");
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
console.log();
|
|
85
|
-
}
|
|
86
75
|
const tasks = new Listr(
|
|
87
76
|
[
|
|
88
77
|
{
|
|
@@ -94,6 +83,11 @@ If you want to stay on the free plan, rerun this command with \`--database postg
|
|
|
94
83
|
return writeFilesTask(files, { overwriteExisting: force });
|
|
95
84
|
}
|
|
96
85
|
},
|
|
86
|
+
// Add health check api function
|
|
87
|
+
addFilesTask({
|
|
88
|
+
files: additionalFiles,
|
|
89
|
+
force
|
|
90
|
+
}),
|
|
97
91
|
printSetupNotes(notes)
|
|
98
92
|
],
|
|
99
93
|
{ rendererOptions: { collapseSubtasks: false } }
|
|
@@ -2,18 +2,16 @@ import path from "path";
|
|
|
2
2
|
import { getPaths } from "../../../../lib/index.js";
|
|
3
3
|
import { getUserApiUrl } from "../helpers/index.js";
|
|
4
4
|
const PROJECT_NAME = path.basename(getPaths().base);
|
|
5
|
-
const RENDER_YAML = (database
|
|
5
|
+
const RENDER_YAML = (database) => {
|
|
6
6
|
const apiUrl = getUserApiUrl().replace(/\/$/, "");
|
|
7
7
|
return `# Quick links to the docs:
|
|
8
|
-
# -
|
|
9
|
-
# - Render's
|
|
10
|
-
# https://render.com/docs/deploy-redwood
|
|
11
|
-
# - Render's Blueprint spec: https://render.com/docs/blueprint-spec
|
|
8
|
+
# - Redwood on Render: https://render.com/docs/deploy-redwood
|
|
9
|
+
# - Render's Blueprint spec: https://render.com/docs/yaml-spec
|
|
12
10
|
|
|
13
11
|
services:
|
|
14
12
|
- name: ${PROJECT_NAME}-web
|
|
15
13
|
type: web
|
|
16
|
-
|
|
14
|
+
env: static
|
|
17
15
|
buildCommand: npm install --global corepack && yarn install && yarn cedar deploy render web
|
|
18
16
|
staticPublishPath: ./web/dist
|
|
19
17
|
|
|
@@ -24,16 +22,11 @@ services:
|
|
|
24
22
|
routes:
|
|
25
23
|
- type: rewrite
|
|
26
24
|
source: ${apiUrl}/*
|
|
27
|
-
# Replace \`destination\` after your first deploy
|
|
28
|
-
# URL from the Render dashboard:
|
|
25
|
+
# Replace \`destination\` here after your first deploy:
|
|
29
26
|
#
|
|
30
27
|
# \`\`\`
|
|
31
|
-
# destination: https
|
|
28
|
+
# destination: https://my-redwood-project-api.onrender.com/*
|
|
32
29
|
# \`\`\`
|
|
33
|
-
#
|
|
34
|
-
# This can't be filled in automatically \u2014 Render's \`fromService\` only
|
|
35
|
-
# resolves service hosts into \`envVars\`, not into a static site's route
|
|
36
|
-
# destination.
|
|
37
30
|
destination: replace_with_api_url/*
|
|
38
31
|
- type: rewrite
|
|
39
32
|
source: /*
|
|
@@ -41,23 +34,12 @@ services:
|
|
|
41
34
|
|
|
42
35
|
- name: ${PROJECT_NAME}-api
|
|
43
36
|
type: web
|
|
44
|
-
plan:
|
|
45
|
-
|
|
37
|
+
plan: free
|
|
38
|
+
env: node
|
|
46
39
|
region: oregon
|
|
47
40
|
buildCommand: npm install --global corepack && yarn install && yarn cedar build api
|
|
48
41
|
startCommand: yarn cedar deploy render api
|
|
49
42
|
|
|
50
|
-
# Proves the GraphQL server is actually serving, not just that the process
|
|
51
|
-
# is alive. Returns 200 with an \`x-yoga-id\` response header.
|
|
52
|
-
#
|
|
53
|
-
# The route is \`<apiRootPath><graphiQLEndpoint>/health\`, and the value below
|
|
54
|
-
# assumes both defaults (\`/\` and \`/graphql\`). Update it to match if you
|
|
55
|
-
# customize either \u2014 by setting \`CEDAR_API_ROOT_PATH\` in the envVars below,
|
|
56
|
-
# by passing \`apiRootPath\` to \`createServer\` in \`api/src/server.ts\`, or by
|
|
57
|
-
# setting \`graphiQLEndpoint\` in \`api/src/functions/graphql.ts\`. A mismatch
|
|
58
|
-
# here 404s, and Render will not promote the deploy.
|
|
59
|
-
healthCheckPath: /graphql/health
|
|
60
|
-
|
|
61
43
|
envVars:
|
|
62
44
|
${database}
|
|
63
45
|
`;
|
|
@@ -69,19 +51,24 @@ const POSTGRES_YAML = ` - key: DATABASE_URL
|
|
|
69
51
|
|
|
70
52
|
databases:
|
|
71
53
|
- name: ${PROJECT_NAME}-db
|
|
72
|
-
plan: free
|
|
73
54
|
region: oregon`;
|
|
74
55
|
const SQLITE_YAML = ` - key: DATABASE_URL
|
|
75
56
|
value: file:./data/sqlite.db
|
|
76
|
-
# Persistent disks aren't available on Render's free plan, which is why
|
|
77
|
-
# the api service above is on a paid plan when SQLite is selected.
|
|
78
57
|
disk:
|
|
79
58
|
name: sqlite-data
|
|
80
59
|
mountPath: /opt/render/project/src/api/db/data
|
|
81
60
|
sizeGB: 1`;
|
|
61
|
+
const RENDER_HEALTH_CHECK = `// render-health-check
|
|
62
|
+
export const handler = async () => {
|
|
63
|
+
return {
|
|
64
|
+
statusCode: 200,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
82
68
|
export {
|
|
83
69
|
POSTGRES_YAML,
|
|
84
70
|
PROJECT_NAME,
|
|
71
|
+
RENDER_HEALTH_CHECK,
|
|
85
72
|
RENDER_YAML,
|
|
86
73
|
SQLITE_YAML
|
|
87
74
|
};
|
|
@@ -94,8 +94,11 @@ ENV NODE_ENV=production
|
|
|
94
94
|
|
|
95
95
|
# default api serve command
|
|
96
96
|
# ---------
|
|
97
|
-
#
|
|
98
|
-
#
|
|
97
|
+
# If you are using a custom server file, you must use the following
|
|
98
|
+
# command to launch your server instead of the default api-server below.
|
|
99
|
+
# This is important if you intend to configure GraphQL to use Realtime.
|
|
100
|
+
#
|
|
101
|
+
# CMD [ "./api/dist/server.js" ]
|
|
99
102
|
CMD ["node_modules/.bin/cedarjs-server", "api"]
|
|
100
103
|
|
|
101
104
|
# web serve
|
|
@@ -4,8 +4,11 @@ services:
|
|
|
4
4
|
context: .
|
|
5
5
|
dockerfile: ./Dockerfile
|
|
6
6
|
target: api_serve
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# Without a command specified, the Dockerfile's api_serve CMD will be used.
|
|
8
|
+
# If you are using a custom server file, you should either use the following
|
|
9
|
+
# command to launch your server or update the Dockerfile to do so.
|
|
10
|
+
# This is important if you intend to configure GraphQL to use Realtime.
|
|
11
|
+
# command: "./api/dist/server.js"
|
|
9
12
|
ports:
|
|
10
13
|
- '8911:8911'
|
|
11
14
|
depends_on:
|
|
@@ -16,11 +16,8 @@ function isPropertyWithName(node, name) {
|
|
|
16
16
|
function transform(file, api) {
|
|
17
17
|
const j = api.jscodeshift;
|
|
18
18
|
const root = j(file.source);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
apolloProviderElements = root.findJSXElements("RedwoodApolloProvider");
|
|
22
|
-
}
|
|
23
|
-
const graphQLClientConfigCollection = apolloProviderElements.find(
|
|
19
|
+
const redwoodApolloProvider = root.findJSXElements("RedwoodApolloProvider");
|
|
20
|
+
const graphQLClientConfigCollection = redwoodApolloProvider.find(
|
|
24
21
|
j.JSXAttribute,
|
|
25
22
|
{
|
|
26
23
|
name: { name: "graphQLClientConfig" }
|
|
@@ -107,7 +104,7 @@ function transform(file, api) {
|
|
|
107
104
|
cacheConfigValue.properties.push(property);
|
|
108
105
|
}
|
|
109
106
|
graphQLClientConfigCollection.remove();
|
|
110
|
-
|
|
107
|
+
redwoodApolloProvider.get(0).node.openingElement.attributes.push(
|
|
111
108
|
j.jsxAttribute(
|
|
112
109
|
j.jsxIdentifier("graphQLClientConfig"),
|
|
113
110
|
j.jsxExpressionContainer(j.identifier(graphQLClientConfigVariableName))
|
|
@@ -90,20 +90,15 @@ const handler = async ({ force }) => {
|
|
|
90
90
|
title: "Replacing Redwood's Error boundary",
|
|
91
91
|
task: async () => {
|
|
92
92
|
const contentLines = fs.readFileSync(rwPaths.web.app).toString().split("\n");
|
|
93
|
-
const webImportRe = /^import \{ FatalErrorBoundary, ((?:Cedar|Redwood)Provider) \} from '@cedarjs\/web'$/;
|
|
94
93
|
const webImportIndex = contentLines.findLastIndex(
|
|
95
|
-
(line) =>
|
|
94
|
+
(line) => /^import { FatalErrorBoundary, RedwoodProvider } from '@cedarjs\/web'$/.test(
|
|
95
|
+
line
|
|
96
|
+
)
|
|
96
97
|
);
|
|
97
|
-
if (webImportIndex === -1) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Could not find "import { FatalErrorBoundary, CedarProvider } from '@cedarjs/web'" in web/src/App`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
const providerName = contentLines[webImportIndex].match(webImportRe)?.[1];
|
|
103
98
|
contentLines.splice(
|
|
104
99
|
webImportIndex,
|
|
105
100
|
1,
|
|
106
|
-
|
|
101
|
+
"import { RedwoodProvider } from '@cedarjs/web'"
|
|
107
102
|
);
|
|
108
103
|
const boundaryOpenIndex = contentLines.findLastIndex(
|
|
109
104
|
(line) => line.includes("<FatalErrorBoundary page={FatalErrorPage}>")
|