@cedarjs/cli 5.0.6 → 5.0.7-next.219

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.
Files changed (38) hide show
  1. package/README.md +7 -10
  2. package/dist/cfw.js +3 -1
  3. package/dist/commands/build/buildHandler.js +14 -6
  4. package/dist/commands/console.js +5 -3
  5. package/dist/commands/dev/devHandler.js +34 -4
  6. package/dist/commands/dev.js +3 -0
  7. package/dist/commands/experimental/setupOpentelemetryHandler.js +1 -1
  8. package/dist/commands/generate/job/jobHandler.js +5 -7
  9. package/dist/commands/generate/package/packageHandler.js +3 -6
  10. package/dist/commands/generate/scaffold/scaffoldHandler.js +73 -4
  11. package/dist/commands/generate/sdl/sdlHandler.js +9 -33
  12. package/dist/commands/generate/yargsHandlerHelpers.js +0 -6
  13. package/dist/commands/lint.js +1 -61
  14. package/dist/commands/prismaHandler.js +3 -6
  15. package/dist/commands/serve.js +21 -22
  16. package/dist/commands/serveBothHandler.js +4 -4
  17. package/dist/commands/setup/auth/auth.js +1 -1
  18. package/dist/commands/setup/database/database.js +15 -0
  19. package/dist/commands/setup/database/postgres.js +19 -0
  20. package/dist/commands/setup/database/postgresHandler.js +194 -0
  21. package/dist/commands/setup/deploy/helpers/index.js +9 -39
  22. package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +17 -5
  23. package/dist/commands/setup/deploy/providers/renderHandler.js +4 -20
  24. package/dist/commands/setup/deploy/templates/render.js +24 -14
  25. package/dist/commands/setup/docker/templates/Dockerfile.yarn +2 -5
  26. package/dist/commands/setup/docker/templates/docker-compose.dev.yml +1 -1
  27. package/dist/commands/setup/docker/templates/docker-compose.prod.yml +2 -5
  28. package/dist/commands/setup/graphql/features/fragments/appGqlConfigTransform.js +6 -3
  29. package/dist/commands/setup/neon/neonHandler.js +96 -254
  30. package/dist/commands/setup/ui/libraries/tailwindcssHandler.js +0 -1
  31. package/dist/commands/setup.js +2 -1
  32. package/dist/lib/exec.js +5 -8
  33. package/dist/lib/index.js +22 -6
  34. package/dist/lib/updateCheck.js +34 -6
  35. package/dist/telemetry/resource.js +3 -8
  36. package/package.json +20 -21
  37. package/dist/commands/consoleHandler.js +0 -75
  38. /package/dist/commands/setup/{neon → database}/templates/db.ts.template +0 -0
package/README.md CHANGED
@@ -238,12 +238,9 @@ export const builder = (yargs) => {
238
238
 
239
239
  ...
240
240
 
241
- .option('stats', {
242
- default: false,
243
- description: `Use ${terminalLink(
244
- 'Webpack Bundle Analyzer',
245
- 'https://github.com/webpack-contrib/webpack-bundle-analyzer'
246
- )}`,
241
+ .option('prerender', {
242
+ default: true,
243
+ description: 'Prerender after building web',
247
244
  type: 'boolean',
248
245
  })
249
246
  .option('verbose', {
@@ -258,11 +255,11 @@ export const builder = (yargs) => {
258
255
  }
259
256
  ```
260
257
 
261
- These two calls to `options` configure this command to have options `--stats` and `--verbose`:
258
+ These two calls to `options` configure this command to have options `--prerender` and `--verbose`:
262
259
 
263
260
  ```terminal
264
- yarn rw build --stats
265
- yarn rw build --verbose
261
+ yarn cedar build --no-prerender
262
+ yarn cedar build --verbose
266
263
  ```
267
264
 
268
265
  For the full list of what properties you can use to compose the options object, see [options(key, [opt])](https://yargs.js.org/docs/#api-optionskey-opt).
@@ -280,8 +277,8 @@ While `build`'s `handler` is too long to reproduce here in full, to get the poin
280
277
 
281
278
  export const handler = async ({
282
279
  side = ['api', 'web'],
280
+ prerender = true,
283
281
  verbose = false,
284
- stats = false,
285
282
  }) => {
286
283
 
287
284
  ...
package/dist/cfw.js CHANGED
@@ -40,6 +40,8 @@ try {
40
40
  // CEDAR_DISABLE_TELEMETRY
41
41
  env: { CEDAR_CWD: projectPath }
42
42
  });
43
- } catch {
43
+ } catch (e) {
44
44
  console.log();
45
+ const exitCode = e && typeof e === "object" && "exitCode" in e && typeof e.exitCode === "number" ? e.exitCode : 1;
46
+ process.exit(exitCode);
45
47
  }
@@ -181,10 +181,14 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
181
181
  "@cedarjs/vite/bins/cedar-vite-build.mjs"
182
182
  );
183
183
  await execa(
184
- `node ${buildBinPath} --webDir="${cedarPaths.web.base}" --verbose=${verbose}`,
184
+ "node",
185
+ [
186
+ buildBinPath,
187
+ `--webDir=${cedarPaths.web.base}`,
188
+ `--verbose=${verbose}`
189
+ ],
185
190
  {
186
191
  stdio: verbose ? "inherit" : "pipe",
187
- shell: true,
188
192
  cwd: cedarPaths.web.base
189
193
  }
190
194
  );
@@ -223,12 +227,16 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
223
227
  "@cedarjs/vite/bins/cedar-vite-build.mjs"
224
228
  );
225
229
  await execa(
226
- `node ${buildBinPath} --webDir="${cedarPaths.web.base}" --verbose=${verbose}`,
230
+ "node",
231
+ [
232
+ buildBinPath,
233
+ `--webDir=${cedarPaths.web.base}`,
234
+ `--verbose=${verbose}`
235
+ ],
227
236
  {
228
237
  stdio: verbose ? "inherit" : "pipe",
229
- shell: true,
230
- // `cwd` is needed for the package manager (e.g. yarn) to find the
231
- // cedar-vite-build binary
238
+ // `cwd` makes postcss/tailwind config resolution work (see the
239
+ // @NOTE above)
232
240
  // It won't change process.cwd for anything else here, in this
233
241
  // process
234
242
  cwd: cedarPaths.web.base
@@ -1,9 +1,11 @@
1
1
  const command = "console";
2
2
  const aliases = ["c"];
3
- const description = "Launch an interactive Redwood shell (experimental)";
3
+ const description = "Launch an interactive Cedar shell";
4
4
  const handler = async () => {
5
- const { handler: handler2 } = await import("./consoleHandler.js");
6
- return handler2();
5
+ console.log(
6
+ "`cedar console` has been removed from the Cedar CLI.\nRun it as a standalone tool instead:\n\n yarn dlx @cedarjs/console\n npx @cedarjs/console\n pnpm dlx @cedarjs/console\n"
7
+ );
8
+ process.exit(1);
7
9
  };
8
10
  export {
9
11
  aliases,
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import { createRequire } from "node:module";
2
3
  import path from "node:path";
3
4
  import { Writable } from "node:stream";
4
5
  import concurrently from "concurrently";
@@ -7,6 +8,7 @@ import { formatRunBinCommand } from "@cedarjs/cli-helpers/packageManager/display
7
8
  import { shutdownPort } from "@cedarjs/internal/dist/dev";
8
9
  import { generateGqlormArtifacts } from "@cedarjs/internal/dist/generate/gqlormSchema";
9
10
  import { getConfig, getConfigPath } from "@cedarjs/project-config";
11
+ import { getPackageManager } from "@cedarjs/project-config/packageManager";
10
12
  import { errorTelemetry } from "@cedarjs/telemetry";
11
13
  import { exitWithError } from "../../lib/exit.js";
12
14
  import { generatePrismaClient } from "../../lib/generatePrismaClient.js";
@@ -15,13 +17,38 @@ import { getFreePort } from "../../lib/ports.js";
15
17
  import { serverFileExists } from "../../lib/project.js";
16
18
  import { getApiDebugFlag } from "./apiDebugFlag.js";
17
19
  import { getPackageWatchCommands } from "./packageWatchCommands.js";
20
+ const createdRequire = createRequire(import.meta.url);
21
+ function formatViteDevBinCommand(binName, extraNodeArgs = "") {
22
+ let vitePackageJsonPath;
23
+ let vitePackageJson;
24
+ try {
25
+ vitePackageJsonPath = createdRequire.resolve("@cedarjs/vite/package.json");
26
+ vitePackageJson = createdRequire("@cedarjs/vite/package.json");
27
+ } catch (e) {
28
+ const message = e instanceof Error ? e.message : String(e);
29
+ throw new Error(
30
+ `Could not resolve @cedarjs/vite, which the dev server needs to run. Is it installed? (${message})`
31
+ );
32
+ }
33
+ const binRelativePath = vitePackageJson.bin?.[binName];
34
+ if (!binRelativePath) {
35
+ throw new Error(
36
+ `@cedarjs/vite does not declare a "${binName}" bin. This is a bug in CedarJS.`
37
+ );
38
+ }
39
+ const binPath = path.join(path.dirname(vitePackageJsonPath), binRelativePath);
40
+ const nodeLauncher = getPackageManager() === "yarn" ? "yarn node" : "node";
41
+ const flags = extraNodeArgs ? `${extraNodeArgs} ` : "";
42
+ return `${nodeLauncher} ${flags}"${binPath}"`;
43
+ }
18
44
  const handler = async ({
19
45
  workspace = ["api", "web", "packages/*"],
20
46
  forward = "",
21
47
  generate = true,
22
48
  apiDebugPort,
23
49
  debugBrk,
24
- ud = false
50
+ ud = false,
51
+ nodeArgs = ""
25
52
  }) => {
26
53
  recordTelemetryAttributes({
27
54
  command: "dev",
@@ -174,7 +201,7 @@ const handler = async ({
174
201
  return null;
175
202
  }
176
203
  return [
177
- `${formatRunBinCommand("cross-env", ["NODE_ENV=development", "cedar-unified-dev"])}`,
204
+ formatViteDevBinCommand("cedar-unified-dev", nodeArgs),
178
205
  ` --port ${webAvailablePort}`,
179
206
  ` --apiPort ${apiAvailablePort}`,
180
207
  getApiDebugFlag(apiDebugPort, apiAvailablePort),
@@ -230,13 +257,16 @@ const handler = async ({
230
257
  });
231
258
  }
232
259
  if (workspace.includes("web")) {
233
- let webCommand = `${formatRunBinCommand("cross-env", ["NODE_ENV=development", "cedar-vite-dev"])} ${forward}`;
260
+ let webCommand = `${formatViteDevBinCommand("cedar-vite-dev", nodeArgs)} ${forward}`;
234
261
  if (streamingSsrEnabled) {
235
- webCommand = `${formatRunBinCommand("cross-env", ["NODE_ENV=development", "cedar-dev-fe"])} ${forward}`;
262
+ webCommand = `${formatViteDevBinCommand("cedar-dev-fe", nodeArgs)} ${forward}`;
236
263
  }
237
264
  jobs.push({
238
265
  name: "web",
239
266
  command: webCommand,
267
+ env: {
268
+ NODE_ENV: "development"
269
+ },
240
270
  prefixColor: "blue",
241
271
  cwd: cedarPaths.web.base,
242
272
  runWhen: () => fs.existsSync(cedarPaths.web.src)
@@ -31,6 +31,9 @@ const builder = (yargs) => {
31
31
  type: "boolean",
32
32
  default: false,
33
33
  description: "Use the unified Vite dev server that handles both web and API in a single process (experimental)."
34
+ }).option("nodeArgs", {
35
+ type: "string",
36
+ description: 'CLI args to pass to the node process running the web dev server, for example: `--node-args="--inspect --max-old-space-size=8192"`.'
34
37
  }).middleware(() => {
35
38
  const check = checkNodeVersion();
36
39
  if (check.ok) {
@@ -114,7 +114,7 @@ const handler = async ({
114
114
  },
115
115
  task: (_ctx, task) => {
116
116
  task.output = [
117
- "Please add the following to your 'redwoodFastifyGraphQLServer' plugin options to enable OTel for your graphql",
117
+ "Please add the following to your 'cedarFastifyGraphQLServer' plugin options to enable OTel for your graphql",
118
118
  "openTelemetryOptions: {",
119
119
  " resolvers: true,",
120
120
  " result: true,",
@@ -97,13 +97,11 @@ const handler = async ({
97
97
  {
98
98
  title: "Cleaning up...",
99
99
  task: () => {
100
- runBinSync("eslint", [
101
- "--fix",
102
- "--config",
103
- `${getPaths().base}/node_modules/@cedarjs/eslint-config/shared.js`,
104
- `${getPaths().api.jobsConfig}`,
105
- ...Object.keys(jobFiles)
106
- ]);
100
+ runBinSync(
101
+ "eslint",
102
+ ["--fix", `${getPaths().api.jobsConfig}`, ...Object.keys(jobFiles)],
103
+ { cwd: getPaths().base }
104
+ );
107
105
  }
108
106
  }
109
107
  ],
@@ -407,12 +407,9 @@ const handler = async ({
407
407
  {
408
408
  title: "Cleaning up...",
409
409
  task: () => {
410
- runBinSync("eslint", [
411
- "--fix",
412
- "--config",
413
- `${getPaths().base}/node_modules/@cedarjs/eslint-config/index.js`,
414
- ...Object.keys(packageFiles)
415
- ]);
410
+ runBinSync("eslint", ["--fix", ...Object.keys(packageFiles)], {
411
+ cwd: getPaths().base
412
+ });
416
413
  }
417
414
  }
418
415
  ],
@@ -42,6 +42,7 @@ import { files as serviceFiles } from "../service/serviceHandler.js";
42
42
  import { customOrDefaultTemplatePath } from "../yargsHandlerHelpers.js";
43
43
  const SKIPPABLE_ASSETS = ["scaffold.css"];
44
44
  const PACKAGE_SET = "Set";
45
+ const PACKAGE_PRIVATE_SET = "PrivateSet";
45
46
  const getIdType = (model) => {
46
47
  return model.fields.find((field) => field.isId)?.type;
47
48
  };
@@ -512,6 +513,65 @@ const addHelperPackages = async (task) => {
512
513
  await removeWorkspacePackages("web", ["humanize-string"]);
513
514
  });
514
515
  };
516
+ const isAuthSetup = () => {
517
+ const extensions = ["ts", "js", "tsx", "jsx"];
518
+ return extensions.some(
519
+ (ext) => fs.existsSync(path.join(getPaths().web.src, "auth." + ext))
520
+ );
521
+ };
522
+ const ROUTE_TAG_RE = /<Route\s+([^>]*?)\/?>/g;
523
+ const extractRouteAttr = (tagAttrs, attrName) => tagAttrs.match(new RegExp(`\\b${attrName}=["']([^"']+)["']`))?.[1];
524
+ const getRoutesFileContent = () => {
525
+ const routesPath = getPaths().web.routes;
526
+ if (!fs.existsSync(routesPath)) {
527
+ return void 0;
528
+ }
529
+ return readFile(routesPath).toString();
530
+ };
531
+ const hasLoginRoute = () => {
532
+ const content = getRoutesFileContent();
533
+ if (!content) {
534
+ return false;
535
+ }
536
+ return Array.from(content.matchAll(ROUTE_TAG_RE)).some(
537
+ ([, attrs]) => extractRouteAttr(attrs, "name") === "login"
538
+ );
539
+ };
540
+ const findUnprotectedLandingPageRouteName = () => {
541
+ const content = getRoutesFileContent();
542
+ if (!content) {
543
+ return void 0;
544
+ }
545
+ const privateSetRanges = Array.from(
546
+ content.matchAll(/<PrivateSet\b[^>]*>([\s\S]*?)<\/PrivateSet>/g)
547
+ ).map((match) => ({
548
+ start: match.index ?? 0,
549
+ end: (match.index ?? 0) + match[0].length
550
+ }));
551
+ for (const match of content.matchAll(ROUTE_TAG_RE)) {
552
+ const [, attrs] = match;
553
+ if (extractRouteAttr(attrs, "path") !== "/") {
554
+ continue;
555
+ }
556
+ const tagStart = match.index ?? 0;
557
+ const isProtected = privateSetRanges.some(
558
+ (range) => tagStart >= range.start && tagStart < range.end
559
+ );
560
+ if (!isProtected) {
561
+ return extractRouteAttr(attrs, "name");
562
+ }
563
+ }
564
+ return void 0;
565
+ };
566
+ const getUnauthenticatedRedirectRoute = () => {
567
+ if (!isAuthSetup()) {
568
+ return void 0;
569
+ }
570
+ if (hasLoginRoute()) {
571
+ return "login";
572
+ }
573
+ return findUnprotectedLandingPageRouteName();
574
+ };
515
575
  const addSetImport = (task) => {
516
576
  const routesPath = getPaths().web.routes;
517
577
  const routesContent = readFile(routesPath).toString();
@@ -525,15 +585,19 @@ const addSetImport = (task) => {
525
585
  return void 0;
526
586
  }
527
587
  const routerImports = importContent.replace(/\s/g, "").split(",");
528
- if (routerImports.includes(PACKAGE_SET)) {
588
+ const namesToImport = [
589
+ PACKAGE_SET,
590
+ ...getUnauthenticatedRedirectRoute() ? [PACKAGE_PRIVATE_SET] : []
591
+ ].filter((name) => !routerImports.includes(name));
592
+ if (!namesToImport.length) {
529
593
  return "Skipping Set import";
530
594
  }
531
595
  const newRoutesContent = routesContent.replace(
532
596
  cedarRouterImport,
533
- importStart + spacing + PACKAGE_SET + `,` + spacing + importContent + importEnd
597
+ importStart + spacing + namesToImport.join("," + spacing) + `,` + spacing + importContent + importEnd
534
598
  );
535
599
  writeFile(routesPath, newRoutesContent, { overwriteExisting: true });
536
- return "Added Set import to Routes.{jsx,tsx}";
600
+ return `Added ${namesToImport.join(", ")} import to Routes.{jsx,tsx}`;
537
601
  };
538
602
  const addScaffoldSetToRouter = async (model, scaffoldPath) => {
539
603
  const templateNames = getTemplateStrings(model, scaffoldPath);
@@ -542,10 +606,12 @@ const addScaffoldSetToRouter = async (model, scaffoldPath) => {
542
606
  const titleTo = templateNames.pluralRouteName;
543
607
  const buttonLabel = `New ${nameVars.singularPascalName}`;
544
608
  const buttonTo = templateNames.newRouteName;
609
+ const unauthenticatedRoute = getUnauthenticatedRedirectRoute();
545
610
  return addRoutesToRouterTask(
546
611
  await routes({ model, path: scaffoldPath }),
547
612
  "ScaffoldLayout",
548
- { title, titleTo, buttonLabel, buttonTo }
613
+ { title, titleTo, buttonLabel, buttonTo },
614
+ unauthenticatedRoute ? { unauthenticated: unauthenticatedRoute } : void 0
549
615
  );
550
616
  };
551
617
  const tasks = ({
@@ -664,7 +730,10 @@ const splitPathAndModel = (pathSlashModel) => {
664
730
  };
665
731
  export {
666
732
  files,
733
+ getUnauthenticatedRedirectRoute,
667
734
  handler,
735
+ hasLoginRoute,
736
+ isAuthSetup,
668
737
  routes,
669
738
  shouldUseTailwindCSS,
670
739
  splitPathAndModel,
@@ -46,15 +46,8 @@ const addFieldGraphQLComment = (field, str) => {
46
46
  const modelFieldToSDL = ({
47
47
  field,
48
48
  required = true,
49
- types = {},
50
49
  docs = false
51
50
  }) => {
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
- }
58
51
  const prismaTypeToGraphqlType = {
59
52
  Json: "JSON",
60
53
  Decimal: "Float",
@@ -73,7 +66,7 @@ const modelFieldToSDL = ({
73
66
  const querySDL = (model, docs = false) => {
74
67
  return model.fields.map((field) => modelFieldToSDL({ field, docs }));
75
68
  };
76
- const inputSDL = (model, required, types = {}, docs = false) => {
69
+ const inputSDL = (model, required, docs = false) => {
77
70
  const ignoredFields = DEFAULT_IGNORE_FIELDS_FOR_INPUT;
78
71
  return model.fields.filter((field) => {
79
72
  const idField = model.fields.find((field2) => field2.isId);
@@ -81,21 +74,19 @@ const inputSDL = (model, required, types = {}, docs = false) => {
81
74
  ignoredFields.push(idField.name);
82
75
  }
83
76
  return !ignoredFields.includes(field.name) && field.kind !== "object";
84
- }).map((field) => modelFieldToSDL({ field, required, types, docs }));
77
+ }).map((field) => modelFieldToSDL({ field, required, docs }));
85
78
  };
86
79
  function idInputSDL(idType2, docs) {
87
80
  if (!Array.isArray(idType2)) {
88
81
  return [];
89
82
  }
90
- return idType2.map(
91
- (field) => modelFieldToSDL({ field, required: true, types: {}, docs })
92
- );
83
+ return idType2.map((field) => modelFieldToSDL({ field, required: true, docs }));
93
84
  }
94
- const createInputSDL = (model, types = {}, docs = false) => {
95
- return inputSDL(model, true, types, docs);
85
+ const createInputSDL = (model, docs = false) => {
86
+ return inputSDL(model, true, docs);
96
87
  };
97
- const updateInputSDL = (model, types = {}, docs = false) => {
98
- return inputSDL(model, false, types, docs);
88
+ const updateInputSDL = (model, docs = false) => {
89
+ return inputSDL(model, false, docs);
99
90
  };
100
91
  function idType(model, crud) {
101
92
  if (!crud || !model) {
@@ -131,21 +122,6 @@ const sdlFromSchemaModel = async (name, crud, docs = false) => {
131
122
  );
132
123
  }
133
124
  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
- );
149
125
  const enums = (await Promise.all(
150
126
  model.fields.filter((field) => field.kind === "enum").map(async (field) => {
151
127
  const enumDef = await getEnum(field.type);
@@ -159,8 +135,8 @@ const sdlFromSchemaModel = async (name, crud, docs = false) => {
159
135
  modelName,
160
136
  modelDescription,
161
137
  query: querySDL(model, docs).join("\n "),
162
- createInput: createInputSDL(model, types, docs).join("\n "),
163
- updateInput: updateInputSDL(model, types, docs).join("\n "),
138
+ createInput: createInputSDL(model, docs).join("\n "),
139
+ updateInput: updateInputSDL(model, docs).join("\n "),
164
140
  idInput: idInputSDL(idTypeRes, docs).join("\n "),
165
141
  idType: idType(model, crud),
166
142
  idName: idName(model, crud),
@@ -30,14 +30,8 @@ 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;
34
33
  if (fs.existsSync(customPath)) {
35
34
  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;
41
35
  } else {
42
36
  return defaultPath;
43
37
  }
@@ -1,63 +1,8 @@
1
1
  import fs from "node:fs";
2
- import path from "node:path";
3
2
  import { terminalLink } from "termi-link";
4
3
  import { recordTelemetryAttributes } from "@cedarjs/cli-helpers";
5
4
  import { runBin } from "@cedarjs/cli-helpers/packageManager/exec";
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
- }
5
+ import { getPaths } from "@cedarjs/project-config";
61
6
  const command = "lint [paths..]";
62
7
  const description = "Lint your files";
63
8
  const builder = (yargs) => {
@@ -86,11 +31,6 @@ const handler = async ({
86
31
  format = "stylish"
87
32
  }) => {
88
33
  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
- }
94
34
  try {
95
35
  const sbPath = getPaths().web.storybook;
96
36
  const eslintArgs = [
@@ -49,18 +49,15 @@ const handler = async ({
49
49
  for (const [name, value] of Object.entries(options)) {
50
50
  args.push(name.length > 1 ? `--${name}` : `-${name}`);
51
51
  if (typeof value === "string") {
52
- if (value.split(" ").length > 1) {
53
- args.push(`"${value}"`);
54
- } else {
55
- args.push(value);
56
- }
52
+ args.push(value);
57
53
  } else if (typeof value === "number") {
58
54
  args.push(String(value));
59
55
  }
60
56
  }
57
+ const displayCommand = args.map((arg) => arg.includes(" ") ? `"${arg}"` : arg).join(" ");
61
58
  console.log();
62
59
  console.log(c.note("Running Prisma CLI..."));
63
- console.log(c.underline(`$ <pm exec> prisma ${args.join(" ")}`));
60
+ console.log(c.underline(`$ <pm exec> prisma ${displayCommand}`));
64
61
  console.log();
65
62
  try {
66
63
  runTransitiveBinSync("prisma", args, {
@@ -6,11 +6,18 @@ 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";
10
9
  import * as webServerCLIConfig from "@cedarjs/web-server";
11
10
  import { getPaths, getConfig } from "../lib/index.js";
12
11
  import { serverFileExists } from "../lib/project.js";
13
12
  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
+ }
14
21
  function resolveUDEntryPath() {
15
22
  const base = path.join(getPaths().api.dist, "ud", "index");
16
23
  for (const ext of [".mjs", ".js"]) {
@@ -91,17 +98,13 @@ const builder = async (yargs) => {
91
98
  process.exit(1);
92
99
  }
93
100
  if (serverFileExists()) {
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
- );
101
+ refuseServerFileUnderUD();
99
102
  }
100
103
  const { getAPIHost, getAPIPort, getWebHost, getWebPort } = await import("@cedarjs/api-server/cliHelpers");
101
104
  const apiPort = argv.apiPort ?? getAPIPort();
102
105
  const apiHost = argv.apiHost ?? getAPIHost();
103
- const webPort = argv.webPort ?? getWebPort();
104
- const webHost = argv.webHost ?? getWebHost();
106
+ const webPort = argv.webPort ?? getWebPort({ isPublicSide: true });
107
+ const webHost = argv.webHost ?? getWebHost({ isPublicSide: true });
105
108
  const apiRootPath = argv.apiRootPath ?? "/";
106
109
  const apiTarget = `http://${apiHost.includes(":") ? `[${apiHost}]` : apiHost}:${apiPort}`;
107
110
  const { serveStatic } = await import("srvx/static");
@@ -162,12 +165,7 @@ const builder = async (yargs) => {
162
165
  const serveBothHandlers = await import("./serveBothHandler.js");
163
166
  await serveBothHandlers.bothSsrRscServerHandler(argv, rscEnabled);
164
167
  } else {
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
- }
168
+ await bothServerCLIConfig.handler(argv);
171
169
  }
172
170
  }
173
171
  }).command({
@@ -193,7 +191,15 @@ const builder = async (yargs) => {
193
191
  socket: argv.socket,
194
192
  apiRootPath: argv.apiRootPath
195
193
  });
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;
196
199
  if (argv.ud) {
200
+ if (serverFileExists()) {
201
+ refuseServerFileUnderUD();
202
+ }
197
203
  const udEntryPath = resolveUDEntryPath();
198
204
  if (!udEntryPath) {
199
205
  console.error(
@@ -203,8 +209,6 @@ const builder = async (yargs) => {
203
209
  );
204
210
  process.exit(1);
205
211
  }
206
- const apiPort = argv.port ?? parseInt(process.env.PORT ?? "8911", 10);
207
- const apiHost = argv.host ?? process.env.HOST ?? "localhost";
208
212
  process.stdout.write(
209
213
  `API server starting at http://${apiHost}:${apiPort}...`
210
214
  );
@@ -219,12 +223,7 @@ const builder = async (yargs) => {
219
223
  const { apiServerFileHandler } = await import("./serveApiHandler.js");
220
224
  await apiServerFileHandler(argv);
221
225
  } else {
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
- }
226
+ await apiServerCLIConfig.handler(argv);
228
227
  }
229
228
  }
230
229
  }).command({