@cedarjs/cli 7.1.0-rc.34 → 7.1.1-next.468

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 (194) hide show
  1. package/dist/commands/build/buildHandler.js +2 -1
  2. package/dist/commands/build.js +1 -1
  3. package/dist/commands/check.js +2 -1
  4. package/dist/commands/deploy/baremetal/baremetalHandler.js +364 -73
  5. package/dist/commands/deploy/baremetal/monitors.js +66 -0
  6. package/dist/commands/deploy/baremetal.js +7 -1
  7. package/dist/commands/deploy/flightcontrolHandler.js +1 -1
  8. package/dist/commands/deploy/helpers/deployHandler.js +1 -1
  9. package/dist/commands/deploy/netlify.js +1 -1
  10. package/dist/commands/deploy/renderHandler.js +1 -1
  11. package/dist/commands/deploy/serverlessHandler.js +2 -1
  12. package/dist/commands/deploy/vercel.js +1 -1
  13. package/dist/commands/destroy/handlerHelpers.js +1 -1
  14. package/dist/commands/destroy/page/pageHandler.js +2 -1
  15. package/dist/commands/destroy/scaffold/scaffoldHandler.js +65 -26
  16. package/dist/commands/destroy/sdl/sdlHandler.js +2 -1
  17. package/dist/commands/dev/devHandler.js +3 -4
  18. package/dist/commands/dev/packageWatchCommands.js +8 -3
  19. package/dist/commands/dev.js +1 -1
  20. package/dist/commands/execHandler.js +2 -1
  21. package/dist/commands/experimental/live-queries/liveQueries.js +1 -1
  22. package/dist/commands/experimental/live-queries/liveQueriesHandler.js +3 -3
  23. package/dist/commands/experimental/setupInngest.js +1 -1
  24. package/dist/commands/experimental/setupInngestHandler.js +1 -1
  25. package/dist/commands/experimental/setupOpentelemetry.js +1 -1
  26. package/dist/commands/experimental/setupOpentelemetryHandler.js +2 -1
  27. package/dist/commands/experimental/setupReactCompiler.js +1 -1
  28. package/dist/commands/experimental/setupReactCompilerHandler.js +4 -4
  29. package/dist/commands/experimental/setupRsc.js +1 -1
  30. package/dist/commands/experimental/setupRscHandler.js +2 -1
  31. package/dist/commands/experimental/setupStreamingSsr.js +1 -1
  32. package/dist/commands/experimental/setupStreamingSsrHandler.js +3 -2
  33. package/dist/commands/generate/cell/cell.js +20 -0
  34. package/dist/commands/generate/cell/cellHandler.js +68 -20
  35. package/dist/commands/generate/cell/templates/cell.tsx.template +21 -2
  36. package/dist/commands/generate/cell/templates/cellFragment.tsx.template +43 -0
  37. package/dist/commands/generate/cell/templates/cellList.tsx.template +21 -2
  38. package/dist/commands/generate/cell/templates/storiesFragment.tsx.template +23 -0
  39. package/dist/commands/generate/cell/templates/test.js.template +1 -1
  40. package/dist/commands/generate/cell/templates/testFragment.js.template +30 -0
  41. package/dist/commands/generate/dataMigration/dataMigration.js +2 -1
  42. package/dist/commands/generate/dbAuth/dbAuth.js +6 -2
  43. package/dist/commands/generate/dbAuth/dbAuthHandler.js +80 -17
  44. package/dist/commands/generate/dbAuth/templates/changePassword.tsx.template +131 -0
  45. package/dist/commands/generate/dbAuth/templates/login.oauth.tsx.template +164 -0
  46. package/dist/commands/generate/dbAuth/templates/login.webAuthn.tsx.template +20 -12
  47. package/dist/commands/generate/dbAuth/templates/signup.oauth.tsx.template +147 -0
  48. package/dist/commands/generate/directive/directiveHandler.js +2 -1
  49. package/dist/commands/generate/function/functionHandler.js +2 -1
  50. package/dist/commands/generate/helpers.js +26 -0
  51. package/dist/commands/generate/job/jobHandler.js +2 -1
  52. package/dist/commands/generate/model/modelHandler.js +2 -1
  53. package/dist/commands/generate/ogImage/ogImageHandler.js +2 -1
  54. package/dist/commands/generate/ogImage/templates/ogImage.og.tsx.template +1 -1
  55. package/dist/commands/generate/package/filesTask.js +9 -12
  56. package/dist/commands/generate/package/packageHandler.js +3 -4
  57. package/dist/commands/generate/package/templates/tsconfig.json.template +1 -0
  58. package/dist/commands/generate/page/pageHandler.js +2 -1
  59. package/dist/commands/generate/realtime/realtime.js +1 -1
  60. package/dist/commands/generate/realtime/realtimeHandler.js +2 -9
  61. package/dist/commands/generate/realtime/templates/liveQueries/blank/blank.service.ts.template +12 -4
  62. package/dist/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template +2 -2
  63. package/dist/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template +4 -4
  64. package/dist/commands/generate/scaffold/scaffold.js +1 -1
  65. package/dist/commands/generate/scaffold/scaffoldHandler.js +93 -49
  66. package/dist/commands/generate/scaffold/templates/assets/scaffold.tailwind.css.template +12 -7
  67. package/dist/commands/generate/scaffold/templates/components/EditNameCell.tsx.template +5 -7
  68. package/dist/commands/generate/scaffold/templates/components/NameForm.tsx.template +10 -9
  69. package/dist/commands/generate/scaffold/templates/layouts/NamesScaffoldLayout.tsx.template +18 -0
  70. package/dist/commands/generate/scaffold/templates/lib/formatters.test.tsx.template +11 -0
  71. package/dist/commands/generate/scaffold/templates/lib/formatters.tsx.template +16 -4
  72. package/dist/commands/generate/script/scriptHandler.js +2 -1
  73. package/dist/commands/generate/script/templates/tsconfig.json.template +1 -0
  74. package/dist/commands/generate/sdl/sdlHandler.js +10 -4
  75. package/dist/commands/generate/secret/secret.js +1 -1
  76. package/dist/commands/generate/service/serviceHandler.js +79 -49
  77. package/dist/commands/generate/service/templates/service.ts.template +40 -13
  78. package/dist/commands/generate/service/templates/test.ts.template +8 -6
  79. package/dist/commands/generate/yargsCommandHelpers.js +1 -1
  80. package/dist/commands/generate/yargsHandlerHelpers.js +2 -1
  81. package/dist/commands/generate.js +1 -1
  82. package/dist/commands/info.js +1 -1
  83. package/dist/commands/jobsHandler.js +5 -3
  84. package/dist/commands/lint.js +1 -1
  85. package/dist/commands/prerenderHandler.js +11 -4
  86. package/dist/commands/prismaHandler.js +7 -2
  87. package/dist/commands/record/init.js +1 -1
  88. package/dist/commands/serve.js +2 -1
  89. package/dist/commands/setup/auth/auth.js +2 -4
  90. package/dist/commands/setup/cache/cache.js +1 -1
  91. package/dist/commands/setup/cache/cacheHandler.js +2 -1
  92. package/dist/commands/setup/cache/templates/memcached.ts.template +14 -17
  93. package/dist/commands/setup/cache/templates/redis.ts.template +12 -20
  94. package/dist/commands/setup/database/postgres.js +1 -1
  95. package/dist/commands/setup/database/postgresHandler.js +3 -1
  96. package/dist/commands/setup/deploy/providers/baremetal.js +7 -0
  97. package/dist/commands/setup/deploy/providers/baremetalHandler.js +130 -21
  98. package/dist/commands/setup/deploy/providers/coherence.js +1 -1
  99. package/dist/commands/setup/deploy/providers/coherenceHandler.js +7 -6
  100. package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +2 -1
  101. package/dist/commands/setup/deploy/providers/netlifyHandler.js +2 -1
  102. package/dist/commands/setup/deploy/providers/renderHandler.js +2 -1
  103. package/dist/commands/setup/deploy/providers/serverlessHandler.js +2 -1
  104. package/dist/commands/setup/deploy/providers/universalDeployHandler.js +4 -6
  105. package/dist/commands/setup/deploy/providers/vercelHandler.js +2 -1
  106. package/dist/commands/setup/deploy/templates/baremetal.js +119 -9
  107. package/dist/commands/setup/deploy/templates/netlifyUD.js +3 -0
  108. package/dist/commands/setup/docker/docker.js +1 -1
  109. package/dist/commands/setup/docker/dockerHandler.js +2 -1
  110. package/dist/commands/setup/generator/generator.js +1 -1
  111. package/dist/commands/setup/generator/generatorHandler.js +1 -1
  112. package/dist/commands/setup/graphql/features/fragments/fragments.js +1 -1
  113. package/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js +3 -5
  114. package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocuments.js +1 -1
  115. package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js +2 -1
  116. package/dist/commands/setup/i18n/i18n.js +1 -1
  117. package/dist/commands/setup/i18n/i18nHandler.js +1 -1
  118. package/dist/commands/setup/jobs/jobs.js +1 -1
  119. package/dist/commands/setup/jobs/jobsHandler.js +4 -1
  120. package/dist/commands/setup/mailer/mailer.js +1 -1
  121. package/dist/commands/setup/mailer/mailerHandler.js +2 -1
  122. package/dist/commands/setup/middleware/ogImage/ogImage.js +1 -1
  123. package/dist/commands/setup/middleware/ogImage/ogImageHandler.js +2 -1
  124. package/dist/commands/setup/monitoring/sentry/sentry.js +1 -1
  125. package/dist/commands/setup/monitoring/sentry/sentryHandler.js +13 -9
  126. package/dist/commands/setup/monitoring/sentry/templates/sentryApi.ts.template +1 -4
  127. package/dist/commands/setup/monitoring/sentry/templates/sentryWeb.ts.template +7 -4
  128. package/dist/commands/setup/neon/neon.js +1 -1
  129. package/dist/commands/setup/neon/neonHandler.js +5 -9
  130. package/dist/commands/setup/package/package.js +1 -1
  131. package/dist/commands/setup/package/packageHandler.js +4 -4
  132. package/dist/commands/setup/realtime/addRealtimeToGraphql.js +1 -1
  133. package/dist/commands/setup/realtime/realtime.js +1 -1
  134. package/dist/commands/setup/realtime/realtimeHandler.js +4 -16
  135. package/dist/commands/setup/realtime/templates/defer/fastAndSlowFields/fastAndSlowFields.ts.template +4 -1
  136. package/dist/commands/setup/realtime/templates/liveQueries/auctions/auctions.ts.template +12 -4
  137. package/dist/commands/setup/realtime/templates/subscriptions/countdown/countdown.ts.template +1 -1
  138. package/dist/commands/setup/realtime/templates/subscriptions/newMessage/newMessage.ts.template +2 -2
  139. package/dist/commands/setup/server-file/serverFile.js +1 -1
  140. package/dist/commands/setup/server-file/serverFileHandler.js +2 -1
  141. package/dist/commands/setup/tenancy/authCodemod.js +66 -0
  142. package/dist/commands/setup/tenancy/dbCodemod.js +64 -0
  143. package/dist/commands/setup/tenancy/graphqlCodemod.js +88 -0
  144. package/dist/commands/setup/tenancy/preflight.js +49 -0
  145. package/dist/commands/setup/tenancy/schemaPrisma.js +81 -0
  146. package/dist/commands/setup/tenancy/signupCodemod.js +154 -0
  147. package/dist/commands/setup/tenancy/templates/InvitePage.tsx.template +72 -0
  148. package/dist/commands/setup/tenancy/templates/OrgScope.tsx.template +40 -0
  149. package/dist/commands/setup/tenancy/templates/ensureDefaultOrganizations.dataMigration.ts.template +39 -0
  150. package/dist/commands/setup/tenancy/templates/organizations.scenarios.ts.template +57 -0
  151. package/dist/commands/setup/tenancy/templates/organizations.sdl.ts.template +89 -0
  152. package/dist/commands/setup/tenancy/templates/organizations.test.ts.template +381 -0
  153. package/dist/commands/setup/tenancy/templates/organizations.ts.template +394 -0
  154. package/dist/commands/setup/tenancy/templates/requireMembership.test.ts.template +35 -0
  155. package/dist/commands/setup/tenancy/templates/requireMembership.ts.template +25 -0
  156. package/dist/commands/setup/tenancy/tenancy.js +30 -0
  157. package/dist/commands/setup/tenancy/tenancyHandler.js +425 -0
  158. package/dist/commands/setup/ui/libraries/chakra-uiHandler.js +2 -1
  159. package/dist/commands/setup/ui/libraries/mantineHandler.js +2 -1
  160. package/dist/commands/setup/ui/libraries/tailwindcssHandler.js +63 -82
  161. package/dist/commands/setup/uploads/gitignore.js +17 -0
  162. package/dist/commands/setup/uploads/schemaPrisma.js +41 -0
  163. package/dist/commands/setup/uploads/serverFile.js +217 -0
  164. package/dist/commands/setup/uploads/templates/requireUploadToken.ts.template +15 -0
  165. package/dist/commands/setup/uploads/templates/srcLibUploads.ts.template +23 -24
  166. package/dist/commands/setup/uploads/templates/uploads.sdl.ts.template +79 -0
  167. package/dist/commands/setup/uploads/templates/uploads.ts.template +98 -0
  168. package/dist/commands/setup/uploads/templates/withDataUri.ts.template +14 -0
  169. package/dist/commands/setup/uploads/templates/withSignedUrl.ts.template +16 -0
  170. package/dist/commands/setup/uploads/uploads.js +49 -7
  171. package/dist/commands/setup/uploads/uploadsHandler.js +277 -69
  172. package/dist/commands/setup.js +2 -1
  173. package/dist/commands/studio.js +1 -1
  174. package/dist/commands/test/testHandler.js +25 -71
  175. package/dist/commands/test/viteVersionCheck.js +75 -0
  176. package/dist/commands/test.js +10 -18
  177. package/dist/commands/type-checkHandler.js +1 -1
  178. package/dist/commands/upgrade/preUpgradeScripts.js +3 -3
  179. package/dist/commands/upgrade/upgrade.js +1 -1
  180. package/dist/commands/upgrade/upgradeHandler.js +2 -1
  181. package/dist/index.js +5 -8
  182. package/dist/lib/exit.js +1 -1
  183. package/dist/lib/index.js +16 -31
  184. package/dist/lib/loadEnvFiles.js +1 -1
  185. package/dist/lib/plugin.js +1 -1
  186. package/dist/lib/test.js +1 -1
  187. package/dist/lib/updateCheck.js +4 -4
  188. package/dist/middleware/checkNodeVersion.js +4 -4
  189. package/package.json +27 -27
  190. package/dist/commands/setup/ui/templates/postcss.config.cjs.template +0 -9
  191. package/dist/commands/setup/uploads/dbCodemod.js +0 -28
  192. package/dist/commands/setup/uploads/templates/signedUrl.ts.template +0 -21
  193. package/dist/commands/test/testHandlerEsm.js +0 -109
  194. package/dist/commands/testEsm.js +0 -41
@@ -4,45 +4,33 @@ import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
4
4
  import execa from "execa";
5
5
  import { Listr } from "listr2";
6
6
  import { terminalLink } from "termi-link";
7
- import { recordTelemetryAttributes, colors as c } from "@cedarjs/cli-helpers";
7
+ import { colors as c } from "@cedarjs/cli-helpers/colors";
8
8
  import { formatCedarCommand } from "@cedarjs/cli-helpers/packageManager/display";
9
- import { runBin } from "@cedarjs/cli-helpers/packageManager/exec";
10
9
  import {
11
10
  addRootPackages,
12
11
  addWorkspacePackages
13
12
  } from "@cedarjs/cli-helpers/packageManager/packages";
13
+ import { recordTelemetryAttributes } from "@cedarjs/cli-helpers/telemetry";
14
14
  import { errorTelemetry } from "@cedarjs/telemetry";
15
15
  import { getPaths, usingVSCode } from "../../../../lib/index.js";
16
- const tailwindDirectives = [
17
- "@tailwind base;",
18
- "@tailwind components;",
19
- "@tailwind utilities;"
20
- ];
21
- const tailwindDirectivesExist = (indexCSS) => tailwindDirectives.every(
22
- (tailwindDirective) => indexCSS.includes(tailwindDirective)
23
- );
24
- const tailwindImportsAndNotes = [
16
+ import { insertPluginsBeforeCedar } from "../../deploy/helpers/index.js";
17
+ const tailwindImport = "@import 'tailwindcss';";
18
+ const tailwindImportRegExp = /@import\s+['"]tailwindcss['"]/;
19
+ const tailwindImportAndNotes = [
25
20
  "/**",
26
21
  " * START --- SETUP TAILWINDCSS EDIT",
27
22
  " *",
28
- ` * \`${formatCedarCommand(["setup", "ui", "tailwindcss"])}\` placed these directives here`,
23
+ ` * \`${formatCedarCommand(["setup", "ui", "tailwindcss"])}\` placed this import here`,
29
24
  " * to inject Tailwind's styles into your CSS.",
30
25
  " * For more information, see: https://tailwindcss.com/docs/installation",
31
26
  " */",
32
- ...tailwindDirectives,
27
+ tailwindImport,
33
28
  "/**",
34
29
  " * END --- SETUP TAILWINDCSS EDIT",
35
30
  " */\n"
36
31
  ];
37
- const recommendedVSCodeExtensions = [
38
- "csstools.postcss",
39
- "bradlc.vscode-tailwindcss"
40
- ];
32
+ const recommendedVSCodeExtensions = ["bradlc.vscode-tailwindcss"];
41
33
  const recommendationTexts = {
42
- "csstools.postcss": terminalLink(
43
- "PostCSS Language Support",
44
- "https://marketplace.visualstudio.com/items?itemName=csstools.postcss"
45
- ),
46
34
  "bradlc.vscode-tailwindcss": terminalLink(
47
35
  "Tailwind CSS IntelliSense",
48
36
  "https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss"
@@ -86,9 +74,8 @@ const handler = async ({
86
74
  const rwPaths = getPaths();
87
75
  const projectPackages = ["prettier-plugin-tailwindcss@^0.8.0"];
88
76
  const webWorkspacePackages = [
89
- "postcss",
90
- "tailwindcss@^3.4.17",
91
- "autoprefixer"
77
+ "tailwindcss@^4.3.3",
78
+ "@tailwindcss/vite@^4.3.3"
92
79
  ];
93
80
  const tasks = new Listr(
94
81
  [
@@ -139,62 +126,45 @@ const handler = async ({
139
126
  }
140
127
  },
141
128
  {
142
- title: "Configuring PostCSS...",
143
- task: () => {
144
- const postCSSConfigPath = rwPaths.web.postcss;
145
- if (!force && fs.existsSync(postCSSConfigPath)) {
129
+ title: "Adding the Tailwind CSS plugin to the Vite config...",
130
+ task: (_ctx, task) => {
131
+ const viteConfigPath = rwPaths.web.viteConfig;
132
+ if (!viteConfigPath || !fs.existsSync(viteConfigPath)) {
133
+ throw new Error("Could not find web/vite.config.{ts,js}");
134
+ }
135
+ const viteConfig = fs.readFileSync(viteConfigPath, "utf-8");
136
+ if (viteConfig.includes("@tailwindcss/vite")) {
137
+ task.skip("The Tailwind CSS Vite plugin is already configured");
138
+ return;
139
+ }
140
+ const viteConfigWithPlugin = insertPluginsBeforeCedar({
141
+ content: viteConfig,
142
+ pluginCodes: ["tailwindcss()"]
143
+ });
144
+ if (!viteConfigWithPlugin) {
146
145
  throw new Error(
147
- "PostCSS config already exists.\nUse --force to override existing config."
146
+ `Could not find \`cedar()\` in the plugins array in ${viteConfigPath}.
147
+ Add \`import tailwindcss from '@tailwindcss/vite'\` to the top of the file and \`tailwindcss()\` to the plugins array yourself, and then run this command again.`
148
148
  );
149
- } else {
150
- const postCSSConfig = fs.readFileSync(
151
- path.join(
152
- import.meta.dirname,
153
- "../templates/postcss.config.cjs.template"
154
- ),
155
- "utf-8"
156
- );
157
- fs.mkdirSync(path.dirname(postCSSConfigPath), { recursive: true });
158
- return fs.writeFileSync(postCSSConfigPath, postCSSConfig);
159
149
  }
160
- }
161
- },
162
- {
163
- title: "Initializing Tailwind CSS...",
164
- task: async () => {
165
- const tailwindConfigPath = path.join(
166
- rwPaths.web.config,
167
- "tailwind.config.cjs"
150
+ const tailwindPluginImport = "import tailwindcss from '@tailwindcss/vite'\n";
151
+ const importAnchor = /^import .* from ['"]vite['"]/m.exec(viteConfigWithPlugin) ?? /^import /m.exec(viteConfigWithPlugin);
152
+ const insertAt = importAnchor?.index ?? 0;
153
+ fs.writeFileSync(
154
+ viteConfigPath,
155
+ viteConfigWithPlugin.slice(0, insertAt) + tailwindPluginImport + viteConfigWithPlugin.slice(insertAt)
168
156
  );
169
- if (fs.existsSync(tailwindConfigPath)) {
170
- if (force) {
171
- fs.unlinkSync(tailwindConfigPath);
172
- } else {
173
- throw new Error(
174
- "Tailwind CSS config already exists.\nUse --force to override existing config."
175
- );
176
- }
177
- }
178
- await runBin("tailwindcss", ["init", tailwindConfigPath], {
179
- cwd: rwPaths.web.base
180
- });
181
- const tailwindConfig = fs.readFileSync(tailwindConfigPath, "utf-8");
182
- const newTailwindConfig = 'const { join } = require("node:path");\n\n' + tailwindConfig.replace(
183
- "content: []",
184
- "content: [join(__dirname, '../src/**/*.{js,jsx,ts,tsx}')]"
185
- ).replace("export default {", "module.exports = {");
186
- fs.writeFileSync(tailwindConfigPath, newTailwindConfig);
187
157
  }
188
158
  },
189
159
  {
190
- title: "Adding directives to index.css...",
160
+ title: "Adding Tailwind CSS import to index.css...",
191
161
  task: (_ctx, task) => {
192
162
  const INDEX_CSS_PATH = path.join(rwPaths.web.src, "index.css");
193
163
  const indexCSS = fs.readFileSync(INDEX_CSS_PATH, "utf-8");
194
- if (tailwindDirectivesExist(indexCSS)) {
195
- task.skip("Directives already exist in index.css");
164
+ if (tailwindImportRegExp.test(indexCSS)) {
165
+ task.skip("Tailwind CSS import already exists in index.css");
196
166
  } else {
197
- const newIndexCSS = tailwindImportsAndNotes.join("\n") + indexCSS;
167
+ const newIndexCSS = tailwindImportAndNotes.join("\n") + indexCSS;
198
168
  fs.writeFileSync(INDEX_CSS_PATH, newIndexCSS);
199
169
  }
200
170
  }
@@ -275,7 +245,9 @@ const handler = async ({
275
245
  "activeClassName",
276
246
  "errorClassName"
277
247
  ];
248
+ const fileAssociations = { "*.css": "tailwindcss" };
278
249
  let newSettingsJson = {
250
+ ["files.associations"]: fileAssociations,
279
251
  ["tailwindCSS.classAttributes"]: classAttributes
280
252
  };
281
253
  if (fs.existsSync(VS_CODE_SETTINGS_PATH)) {
@@ -293,8 +265,13 @@ const handler = async ({
293
265
  const mergedClassAttributes = Array.from(
294
266
  /* @__PURE__ */ new Set([...classAttributes, ...originalTwClassAttributesJson])
295
267
  );
268
+ const existingAssociations = originalSettingsJson["files.associations"];
296
269
  newSettingsJson = {
297
270
  ...originalSettingsJson,
271
+ ["files.associations"]: {
272
+ ...existingAssociations && typeof existingAssociations === "object" && !Array.isArray(existingAssociations) ? existingAssociations : {},
273
+ ...fileAssociations
274
+ },
298
275
  ["tailwindCSS.classAttributes"]: mergedClassAttributes
299
276
  };
300
277
  }
@@ -305,34 +282,38 @@ const handler = async ({
305
282
  }
306
283
  },
307
284
  {
308
- title: "Adding tailwind config entry in prettier...",
285
+ title: "Adding tailwind stylesheet entry in prettier...",
309
286
  task: async (_ctx) => {
310
287
  const prettierConfigPath = path.join(
311
288
  rwPaths.base,
312
289
  "prettier.config.cjs"
313
290
  );
314
291
  const prettierConfig = fs.readFileSync(prettierConfigPath, "utf-8");
315
- const tailwindConfigPath = path.relative(
316
- rwPaths.base,
317
- path.posix.join(rwPaths.web.config, "tailwind.config.cjs")
318
- ).replaceAll("\\", "/");
292
+ const tailwindStylesheetPath = path.relative(rwPaths.base, path.join(rwPaths.web.src, "index.css")).replaceAll("\\", "/");
293
+ const tailwindStylesheetEntry = `tailwindStylesheet: './${tailwindStylesheetPath}',`;
319
294
  let newPrettierConfig = prettierConfig;
320
- if (newPrettierConfig.includes("tailwindConfig: ")) {
321
- if (force) {
322
- newPrettierConfig = newPrettierConfig.replace(
323
- /tailwindConfig: .*(,)?/,
324
- `tailwindConfig: './${tailwindConfigPath}',`
295
+ const stringEntryRegExp = /tailwindStylesheet:\s*(['"`])[^'"`\n]*\1,?/;
296
+ if (newPrettierConfig.includes("tailwindStylesheet")) {
297
+ if (!force) {
298
+ throw new Error(
299
+ "tailwindStylesheet setting already exists in prettier configuration.\nUse --force to override existing config."
325
300
  );
326
- } else {
301
+ }
302
+ if (!stringEntryRegExp.test(newPrettierConfig)) {
327
303
  throw new Error(
328
- "tailwindConfig setting already exists in prettier configuration.\nUse --force to override existing config."
304
+ `Could not update the tailwindStylesheet setting in ${prettierConfigPath}.
305
+ Set it to './${tailwindStylesheetPath}' yourself.`
329
306
  );
330
307
  }
308
+ newPrettierConfig = newPrettierConfig.replace(
309
+ stringEntryRegExp,
310
+ tailwindStylesheetEntry
311
+ );
331
312
  } else {
332
313
  newPrettierConfig = newPrettierConfig.replace(
333
314
  /,(\n\s*)(\}\n?)$/,
334
315
  `,
335
- tailwindConfig: './${tailwindConfigPath}',$1$2`
316
+ ${tailwindStylesheetEntry}$1$2`
336
317
  );
337
318
  }
338
319
  fs.writeFileSync(prettierConfigPath, newPrettierConfig);
@@ -0,0 +1,17 @@
1
+ const UPLOADS_GITIGNORE_ENTRY = "api/.uploads";
2
+ function transformGitignore(source) {
3
+ const activeLines = source.split("\n").map((line) => line.trimEnd()).filter((line) => line.length > 0 && !line.startsWith("#"));
4
+ if (activeLines.includes(UPLOADS_GITIGNORE_ENTRY)) {
5
+ return source;
6
+ }
7
+ if (/^dev\.db\*$/m.test(source)) {
8
+ return source.replace(/^(dev\.db\*)$/m, `$1
9
+ ${UPLOADS_GITIGNORE_ENTRY}`);
10
+ }
11
+ const prefix = source === "" || source.endsWith("\n") ? source : source + "\n";
12
+ return prefix + UPLOADS_GITIGNORE_ENTRY + "\n";
13
+ }
14
+ export {
15
+ UPLOADS_GITIGNORE_ENTRY,
16
+ transformGitignore
17
+ };
@@ -0,0 +1,41 @@
1
+ const UPLOAD_MODEL = `model Upload {
2
+ id String @id @default(cuid())
3
+ /// Name of the storage target, as configured in api/src/lib/uploads
4
+ target String
5
+ /// "pending" | "completed" | "failed"
6
+ status String @default("pending")
7
+ filename String
8
+ mimeType String
9
+ /// Bytes. BigInt because Int caps at 2 GB.
10
+ size BigInt
11
+ /// Provider-specific reference: S3 object key, filename on disk, or null
12
+ /// for files stored inline
13
+ storageKey String?
14
+ /// Inline bytes, only for DB targets
15
+ data Bytes?
16
+ /// The user the file belongs to, when it belongs to one
17
+ userId String?
18
+ /// Id of the upload token that created this row
19
+ tokenId String?
20
+ /// The organization the file belongs to, in multi-tenant apps
21
+ organizationId String?
22
+ createdAt DateTime @default(now())
23
+ updatedAt DateTime @updatedAt
24
+ }`;
25
+ function hasModel(schema, modelName) {
26
+ return new RegExp(`\\bmodel\\s+${modelName}\\s*\\{`).test(schema);
27
+ }
28
+ function addUploadModel(schema) {
29
+ if (hasModel(schema, "Upload")) {
30
+ return schema;
31
+ }
32
+ return `${schema.trimEnd()}
33
+
34
+ ${UPLOAD_MODEL}
35
+ `;
36
+ }
37
+ export {
38
+ UPLOAD_MODEL,
39
+ addUploadModel,
40
+ hasModel
41
+ };
@@ -0,0 +1,217 @@
1
+ import { isBuiltin } from "node:module";
2
+ const START_LINE = /^([ \t]*)await server\.start\(\)/m;
3
+ const FACTORY_IMPORT = /import\s*\{[^}]*\bcreateAuthDecoder\b[^}]*\}\s*from\s*['"](@cedarjs\/auth-[a-z0-9-]+-api)['"]/;
4
+ const DECODER_IMPORT = /import\s*\{[^}]*\bauthDecoder\b[^}]*\}\s*from\s*['"](@cedarjs\/auth-[a-z0-9-]+-api)['"]/;
5
+ function detectServerAuth(graphqlSource) {
6
+ const factory = FACTORY_IMPORT.exec(graphqlSource);
7
+ if (factory) {
8
+ return { decoderPackage: factory[1], usesFactory: true };
9
+ }
10
+ const decoder = DECODER_IMPORT.exec(graphqlSource);
11
+ if (decoder) {
12
+ return { decoderPackage: decoder[1], usesFactory: false };
13
+ }
14
+ return null;
15
+ }
16
+ function importedBindings(source) {
17
+ const names = /* @__PURE__ */ new Set();
18
+ const statements = source.matchAll(
19
+ /^import\s+(type\s+)?([^;]*?)\s+from\s+['"][^'"]+['"]/gm
20
+ );
21
+ for (const [, typeOnly, clause] of statements) {
22
+ if (typeOnly) {
23
+ continue;
24
+ }
25
+ const braces = /\{([^}]*)\}/.exec(clause);
26
+ const defaultOrNamespace = clause.replace(/\{[^}]*\}/, "");
27
+ for (const part of defaultOrNamespace.split(",")) {
28
+ const local = part.replace(/^\s*\*\s+as\s+/, "").trim();
29
+ if (local) {
30
+ names.add(local);
31
+ }
32
+ }
33
+ for (const specifier of braces?.[1].split(",") ?? []) {
34
+ const trimmed = specifier.trim();
35
+ if (!trimmed || trimmed.startsWith("type ")) {
36
+ continue;
37
+ }
38
+ const alias = / as (\w+)$/.exec(trimmed);
39
+ names.add(alias ? alias[1] : trimmed);
40
+ }
41
+ }
42
+ return names;
43
+ }
44
+ function hasBinding(source, name) {
45
+ const declared = new RegExp(
46
+ `^(?:export\\s+)?(?:const|let|var|function)\\s+${name}\\b`,
47
+ "m"
48
+ );
49
+ return importedBindings(source).has(name) || declared.test(source);
50
+ }
51
+ function uploadsServerImports(source, auth) {
52
+ const specs = [];
53
+ if (auth) {
54
+ if (auth.usesFactory) {
55
+ if (!hasBinding(source, "authDecoder")) {
56
+ specs.push({
57
+ names: ["createAuthDecoder"],
58
+ from: auth.decoderPackage
59
+ });
60
+ }
61
+ } else {
62
+ specs.push({ names: ["authDecoder"], from: auth.decoderPackage });
63
+ }
64
+ }
65
+ specs.push({
66
+ names: auth ? ["cedarUploadsPlugin", "createUploadAuthenticator"] : ["cedarUploadsPlugin"],
67
+ from: "@cedarjs/uploads"
68
+ });
69
+ if (auth) {
70
+ const authNames = ["getCurrentUser"];
71
+ if (auth.usesFactory && !hasBinding(source, "authDecoder")) {
72
+ authNames.unshift("cookieName");
73
+ }
74
+ specs.push({ names: authNames, from: "src/lib/auth" });
75
+ }
76
+ specs.push({ names: ["db"], from: "src/lib/db" });
77
+ specs.push({ names: ["targets"], from: "src/lib/uploads" });
78
+ return specs.map((spec) => ({
79
+ ...spec,
80
+ names: spec.names.filter((name) => !hasBinding(source, name))
81
+ })).filter((spec) => spec.names.length > 0).map((spec) => ({
82
+ line: `import { ${spec.names.join(", ")} } from '${spec.from}'`,
83
+ from: spec.from
84
+ }));
85
+ }
86
+ function findImports(lines) {
87
+ const imports = [];
88
+ for (let start = 0; start < lines.length; start++) {
89
+ if (!/^import\b/.test(lines[start])) {
90
+ continue;
91
+ }
92
+ const sideEffect = /^import\s*['"]([^'"]+)['"]/.exec(lines[start]);
93
+ if (sideEffect) {
94
+ imports.push({ start, end: start, from: sideEffect[1] });
95
+ continue;
96
+ }
97
+ for (let end = start; end < lines.length; end++) {
98
+ const from = /\bfrom\s*['"]([^'"]+)['"]/.exec(lines[end]);
99
+ if (from) {
100
+ imports.push({ start, end, from: from[1] });
101
+ start = end;
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ return imports;
107
+ }
108
+ function importGroup(from) {
109
+ if (isBuiltin(from)) {
110
+ return 0;
111
+ }
112
+ if (from === "react") {
113
+ return 1;
114
+ }
115
+ if (from.startsWith("@cedarjs/")) {
116
+ return 3;
117
+ }
118
+ if (from.startsWith("src/")) {
119
+ return /^src\/[^/]+\/.+\.(?:sdl\.)?(?:js|ts)$/.test(from) ? 4 : 5;
120
+ }
121
+ if (from === ".." || from.startsWith("../")) {
122
+ return 6;
123
+ }
124
+ if (/^\.\/?(?:index(?:\.[jt]sx?)?)?$/.test(from)) {
125
+ return 8;
126
+ }
127
+ if (from.startsWith("./")) {
128
+ return 7;
129
+ }
130
+ return 2;
131
+ }
132
+ function compareSpecifiers(a, b) {
133
+ const segmentsA = a.toLowerCase().split("/");
134
+ const segmentsB = b.toLowerCase().split("/");
135
+ for (let i = 0; i < Math.min(segmentsA.length, segmentsB.length); i++) {
136
+ if (segmentsA[i] !== segmentsB[i]) {
137
+ return segmentsA[i] < segmentsB[i] ? -1 : 1;
138
+ }
139
+ }
140
+ return segmentsA.length - segmentsB.length;
141
+ }
142
+ function insertImport(lines, serverImport) {
143
+ const imports = findImports(lines);
144
+ const group = importGroup(serverImport.from);
145
+ const sameGroup = imports.filter((i) => importGroup(i.from) === group);
146
+ if (sameGroup.length > 0) {
147
+ const next = sameGroup.find(
148
+ (i) => compareSpecifiers(i.from, serverImport.from) > 0
149
+ );
150
+ const at2 = next ? next.start : sameGroup[sameGroup.length - 1].end + 1;
151
+ return [...lines.slice(0, at2), serverImport.line, ...lines.slice(at2)];
152
+ }
153
+ const earlier = imports.filter((i) => importGroup(i.from) < group);
154
+ if (earlier.length > 0) {
155
+ const at2 = Math.max(...earlier.map((i) => i.end)) + 1;
156
+ const inserted = ["", serverImport.line];
157
+ if (at2 < lines.length && lines[at2].trim() !== "") {
158
+ inserted.push("");
159
+ }
160
+ return [...lines.slice(0, at2), ...inserted, ...lines.slice(at2)];
161
+ }
162
+ const later = imports.find((i) => importGroup(i.from) > group);
163
+ const at = later ? later.start : 0;
164
+ return [...lines.slice(0, at), serverImport.line, "", ...lines.slice(at)];
165
+ }
166
+ function uploadsServerRegistration(source, auth) {
167
+ if (!auth) {
168
+ return ` await server.register(cedarUploadsPlugin, {
169
+ tokenSecret: process.env.UPLOAD_TOKEN_SECRET,
170
+ targets,
171
+ db,
172
+ // Once the app has auth, bind upload tokens to the logged-in user:
173
+ //
174
+ // authenticate: createUploadAuthenticator({ authDecoder, getCurrentUser }),
175
+ })
176
+ `;
177
+ }
178
+ const decoder = auth.usesFactory && !hasBinding(source, "authDecoder") ? " const authDecoder = createAuthDecoder(cookieName)\n\n" : "";
179
+ return `${decoder} await server.register(cedarUploadsPlugin, {
180
+ tokenSecret: process.env.UPLOAD_TOKEN_SECRET,
181
+ targets,
182
+ db,
183
+ // Binds upload tokens to the logged-in user through the same auth
184
+ // pipeline the GraphQL server uses
185
+ authenticate: createUploadAuthenticator({ authDecoder, getCurrentUser }),
186
+ })
187
+ `;
188
+ }
189
+ function hasUploadsPlugin(source) {
190
+ return /^[ \t]*(?:(?:await|void)\s+|(?:const|let|var)\s+\w+\s*=\s*)?[\w.]+\.register\(\s*cedarUploadsPlugin\b/m.test(
191
+ source
192
+ );
193
+ }
194
+ function addUploadsPlugin(source, { auth = null } = {}) {
195
+ if (hasUploadsPlugin(source)) {
196
+ return source;
197
+ }
198
+ const start = START_LINE.exec(source);
199
+ if (!start) {
200
+ throw new Error("CEDAR_UPLOADS_ERR_NO_START");
201
+ }
202
+ const withImports = uploadsServerImports(source, auth).reduce(insertImport, source.split("\n")).join("\n");
203
+ return withImports.replace(
204
+ START_LINE,
205
+ (line) => `${uploadsServerRegistration(source, auth)}
206
+ ${line}`
207
+ );
208
+ }
209
+ export {
210
+ addUploadsPlugin,
211
+ detectServerAuth,
212
+ hasBinding,
213
+ hasUploadsPlugin,
214
+ importedBindings,
215
+ uploadsServerImports,
216
+ uploadsServerRegistration
217
+ };
@@ -0,0 +1,15 @@
1
+ import { createRequireUploadTokenDirective } from '@cedarjs/uploads'
2
+
3
+ /**
4
+ * Requires a valid upload token in the x-upload-token request header and
5
+ * makes its claims available to the resolver through
6
+ * `getUploadTokenPayload(context)`. The token must have been issued to the
7
+ * requesting user.
8
+ */
9
+ const requireUploadToken = createRequireUploadTokenDirective({
10
+ secret: process.env.UPLOAD_TOKEN_SECRET ?? '',
11
+ })
12
+
13
+ export const schema = requireUploadToken.schema
14
+
15
+ export default requireUploadToken
@@ -1,25 +1,24 @@
1
- import { createUploadsConfig, setupStorage } from '@cedarjs/storage'
2
- import { FileSystemStorage } from '@cedarjs/storage/FileSystemStorage'
3
- import { UrlSigner } from '@cedarjs/storage/UrlSigner'
1
+ __PATH_IMPORT____S3_IMPORTS__import {
2
+ __FS_IMPORT__
3
+ __DB_IMPORT__
4
+ __DB_MAX_IMPORT__
5
+ defineStorageTargets,
6
+ defineUploadProfiles,
7
+ } from '@cedarjs/uploads'
8
+ __GET_PATHS_IMPORT__
9
+ __S3_CLIENT__
10
+ /**
11
+ * Where files live. Each target is a provider instance; the Upload row
12
+ * records which target holds a file so it can be read, served, and deleted
13
+ * later. Look one up by name with `resolveTarget(targets, upload.target)`.
14
+ */
15
+ export const targets = defineStorageTargets({
16
+ __TARGETS__})
4
17
 
5
- const uploadsConfig = createUploadsConfig({
6
- // Configure your fields here
7
- // e.g. modelName: { fields: ['fieldWithUpload']}
8
- })
9
-
10
- export const fsStorage = new FileSystemStorage({
11
- baseDir: './uploads',
12
- })
13
-
14
- export const urlSigner = new UrlSigner({
15
- secret: process.env.UPLOADS_SECRET,
16
- endpoint: '/signedUrl',
17
- })
18
-
19
- const { saveFiles, storagePrismaExtension } = setupStorage({
20
- uploadsConfig,
21
- storageAdapter: fsStorage,
22
- urlSigner,
23
- })
24
-
25
- export { saveFiles, storagePrismaExtension }
18
+ /**
19
+ * What clients may upload. A profile names a target and the constraints the
20
+ * server signs into every upload token issued for it. The client never
21
+ * supplies constraints; it names a profile.
22
+ */
23
+ export const profiles = defineUploadProfiles({
24
+ __PROFILES__})
@@ -0,0 +1,79 @@
1
+ export const schema = gql`
2
+ type Upload {
3
+ id: String!
4
+ target: String!
5
+ status: String!
6
+ filename: String!
7
+ mimeType: String!
8
+ size: BigInt!
9
+ storageKey: String
10
+ userId: String
11
+ organizationId: String
12
+ createdAt: DateTime!
13
+ updatedAt: DateTime!
14
+ }
15
+
16
+ type UploadToken {
17
+ token: String!
18
+ """
19
+ The profile's constraints, echoed for client-side UX (file-picker
20
+ filters, pre-upload validation). The authoritative copy is inside the
21
+ signed token.
22
+ """
23
+ allowedMimeTypes: [String!]!
24
+ maxFileSize: BigInt!
25
+ maxFiles: Int!
26
+ }
27
+
28
+ type PresignedUploadUrl {
29
+ uploadId: String!
30
+ url: String!
31
+ method: String!
32
+ headers: JSON!
33
+ }
34
+
35
+ input CreatePresignedUploadUrlInput {
36
+ filename: String!
37
+ contentType: String!
38
+ size: BigInt!
39
+ }
40
+
41
+ input UploadFileInput {
42
+ filename: String!
43
+ mimeType: String!
44
+ """
45
+ The file's bytes, base64-encoded. Only for profiles whose target stores
46
+ files inline in the database.
47
+ """
48
+ data: String!
49
+ }
50
+
51
+ type Query {
52
+ """
53
+ Issues an upload token for a server-defined profile. The profile name
54
+ must match a key of \`profiles\` in api/src/lib/uploads.
55
+ """
56
+ requestUploadToken(profile: String!): UploadToken! @requireAuth
57
+ }
58
+
59
+ type Mutation {
60
+ """
61
+ Direct-to-storage uploads: returns a presigned URL for one file. Send the
62
+ upload token in the x-upload-token header.
63
+ """
64
+ createPresignedUploadUrl(
65
+ input: CreatePresignedUploadUrlInput!
66
+ ): PresignedUploadUrl! @requireAuth @requireUploadToken
67
+
68
+ """
69
+ Confirms a direct-to-storage upload once the client has PUT the bytes.
70
+ """
71
+ confirmUpload(uploadId: String!): Upload! @requireAuth
72
+
73
+ """
74
+ Stores a small file sent as base64, for profiles whose target keeps files
75
+ inline in the database.
76
+ """
77
+ uploadFile(profile: String!, input: UploadFileInput!): Upload! @requireAuth
78
+ }
79
+ `