@wasp.sh/wasp-cli-darwin-arm64-unknown 0.20.2 → 0.21.0-rc.1

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 (118) hide show
  1. package/data/Cli/starters/basic/README.md +1 -1
  2. package/data/Cli/starters/basic/package.json +10 -7
  3. package/data/Cli/starters/basic/src/App.css +30 -12
  4. package/data/Cli/starters/basic/src/App.tsx +1 -1
  5. package/data/Cli/starters/basic/src/auth/AuthLayout.tsx +3 -1
  6. package/data/Cli/starters/basic/src/auth/email/EmailVerificationPage.tsx +2 -2
  7. package/data/Cli/starters/basic/src/auth/email/LoginPage.tsx +3 -3
  8. package/data/Cli/starters/basic/src/auth/email/PasswordResetPage.tsx +2 -2
  9. package/data/Cli/starters/basic/src/auth/email/SignupPage.tsx +2 -2
  10. package/data/Cli/starters/basic/src/shared/components/Button.tsx +3 -2
  11. package/data/Cli/starters/basic/src/shared/components/Dialog.tsx +23 -41
  12. package/data/Cli/starters/basic/src/shared/components/Header.tsx +2 -2
  13. package/data/Cli/starters/basic/src/shared/components/Input.tsx +25 -28
  14. package/data/Cli/starters/basic/src/tags/components/ColorRadioButton.tsx +8 -9
  15. package/data/Cli/starters/basic/src/tags/components/CreateTagDialog.tsx +29 -29
  16. package/data/Cli/starters/basic/src/tags/components/CreateTagForm.tsx +6 -8
  17. package/data/Cli/starters/basic/src/tasks/components/CreateTaskForm.tsx +2 -2
  18. package/data/Cli/starters/basic/src/tasks/components/TaskListItem.tsx +3 -2
  19. package/data/Cli/starters/basic/vite.config.ts +3 -0
  20. package/data/Cli/starters/minimal/package.json +2 -3
  21. package/data/Cli/starters/minimal/vite.config.ts +2 -0
  22. package/data/Cli/starters/skeleton/src/vite-env.d.ts +2 -6
  23. package/data/Generator/libs/auth/wasp.sh-lib-auth-0.21.0.tgz +0 -0
  24. package/data/Generator/templates/Dockerfile +13 -12
  25. package/data/Generator/templates/sdk/wasp/auth/forms/Auth.tsx +2 -11
  26. package/data/Generator/templates/sdk/wasp/auth/forms/internal/common/LoginSignupForm.tsx +10 -4
  27. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ForgotPasswordForm.tsx +4 -3
  28. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ResetPasswordForm.tsx +4 -4
  29. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/VerifyEmailForm.tsx +3 -4
  30. package/data/Generator/templates/sdk/wasp/auth/forms/internal/social/SocialIcons.tsx +11 -0
  31. package/data/Generator/templates/sdk/wasp/auth/forms/types.ts +0 -6
  32. package/data/Generator/templates/sdk/wasp/auth/jwt.ts +9 -16
  33. package/data/Generator/templates/sdk/wasp/auth/password.ts +1 -36
  34. package/data/Generator/templates/sdk/wasp/auth/utils.ts +2 -0
  35. package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/DefaultRootErrorBoundary.tsx +1 -1
  36. package/data/Generator/templates/sdk/wasp/client/app/components/WaspApp.tsx +45 -0
  37. package/data/Generator/templates/sdk/wasp/client/app/index.tsx +26 -0
  38. package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/OAuthCallback.tsx +9 -8
  39. package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/createAuthRequiredPage.jsx +7 -9
  40. package/data/Generator/templates/sdk/wasp/client/app/router/router.tsx +47 -0
  41. package/data/Generator/templates/sdk/wasp/client/auth/index.ts +3 -0
  42. package/data/Generator/templates/sdk/wasp/client/auth/microsoft.ts +2 -0
  43. package/data/Generator/templates/sdk/wasp/client/auth/ui.ts +3 -0
  44. package/data/Generator/templates/sdk/wasp/client/router/Link.tsx +2 -2
  45. package/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx +10 -12
  46. package/data/Generator/templates/sdk/wasp/client/vite/index.ts +1 -0
  47. package/data/Generator/templates/sdk/wasp/client/vite/plugins/detectServerImports.ts +50 -0
  48. package/data/Generator/templates/sdk/wasp/client/vite/plugins/envFile.ts +112 -0
  49. package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/build.ts +38 -0
  50. package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/dev.ts +35 -0
  51. package/data/Generator/templates/sdk/wasp/client/vite/plugins/typescriptCheck.ts +32 -0
  52. package/data/Generator/templates/{react-app/vite → sdk/wasp/client/vite/plugins}/validateEnv.ts +17 -7
  53. package/data/Generator/templates/sdk/wasp/client/vite/plugins/virtualModules.ts +29 -0
  54. package/data/Generator/templates/sdk/wasp/client/vite/plugins/wasp.ts +36 -0
  55. package/data/Generator/templates/sdk/wasp/client/vite/plugins/waspConfig.ts +56 -0
  56. package/data/Generator/templates/{react-app → sdk/wasp/client/vite/virtual-files/files}/index.html +1 -2
  57. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/index.tsx +34 -0
  58. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx +17 -0
  59. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/index.ts +20 -0
  60. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/resolver.ts +28 -0
  61. package/data/Generator/templates/sdk/wasp/package.json +6 -4
  62. package/data/Generator/templates/sdk/wasp/scripts/copy-assets.js +67 -7
  63. package/data/Generator/templates/sdk/wasp/server/auth/hooks.ts +3 -0
  64. package/data/Generator/templates/sdk/wasp/server/auth/oauth/index.ts +4 -0
  65. package/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/microsoft.ts +23 -0
  66. package/data/Generator/templates/sdk/wasp/server/auth/user.ts +6 -0
  67. package/data/Generator/templates/sdk/wasp/server/env.ts +11 -0
  68. package/data/Generator/templates/sdk/wasp/tsconfig.json +5 -1
  69. package/data/Generator/templates/server/src/auth/providers/config/microsoft.ts +65 -0
  70. package/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts +1 -1
  71. package/data/packages/deploy/dist/common/clientApp.js +4 -11
  72. package/data/packages/deploy/dist/common/terminal.js +7 -0
  73. package/data/packages/deploy/dist/common/waspProject.js +11 -7
  74. package/data/packages/deploy/dist/providers/fly/CommonOps.js +3 -3
  75. package/data/packages/deploy/dist/providers/fly/commands/cmd/cmd.js +1 -1
  76. package/data/packages/deploy/dist/providers/fly/commands/deploy/deploy.js +6 -5
  77. package/data/packages/deploy/dist/providers/fly/commands/setup/setup.js +3 -3
  78. package/data/packages/deploy/dist/providers/fly/index.js +5 -0
  79. package/data/packages/deploy/dist/providers/railway/commands/deploy/client.js +2 -1
  80. package/data/packages/deploy/dist/providers/railway/commands/setup/setup.js +21 -10
  81. package/data/packages/deploy/dist/providers/railway/index.js +5 -0
  82. package/data/packages/deploy/dist/providers/railway/jsonOutputSchemas.js +9 -3
  83. package/data/packages/deploy/dist/providers/railway/railwayService/url.js +8 -1
  84. package/data/packages/studio/dist/public/assets/Flow-_d98T2dd.js +26 -0
  85. package/data/packages/studio/dist/public/assets/index-B6X8EdJH.js +21 -0
  86. package/data/packages/studio/dist/public/assets/index-CXlD_bzV.js +1 -0
  87. package/data/packages/studio/dist/public/assets/index-IWX3d-Jz.css +1 -0
  88. package/data/packages/studio/dist/public/index.html +2 -3
  89. package/package.json +1 -1
  90. package/wasp-bin +0 -0
  91. package/data/Cli/starters/basic/postcss.config.js +0 -6
  92. package/data/Cli/starters/basic/src/shared/components/Portal.tsx +0 -26
  93. package/data/Cli/starters/basic/tailwind.config.js +0 -30
  94. package/data/Generator/templates/react-app/README.md +0 -21
  95. package/data/Generator/templates/react-app/gitignore +0 -23
  96. package/data/Generator/templates/react-app/netlify.toml +0 -8
  97. package/data/Generator/templates/react-app/npmrc +0 -1
  98. package/data/Generator/templates/react-app/package.json +0 -31
  99. package/data/Generator/templates/react-app/public/manifest.json +0 -15
  100. package/data/Generator/templates/react-app/src/index.tsx +0 -47
  101. package/data/Generator/templates/react-app/src/logo.png +0 -0
  102. package/data/Generator/templates/react-app/src/router.tsx +0 -59
  103. package/data/Generator/templates/react-app/src/utils.js +0 -3
  104. package/data/Generator/templates/react-app/src/vite-env.d.ts +0 -1
  105. package/data/Generator/templates/react-app/tsconfig.app.json +0 -28
  106. package/data/Generator/templates/react-app/tsconfig.json +0 -11
  107. package/data/Generator/templates/react-app/tsconfig.vite.json +0 -16
  108. package/data/Generator/templates/react-app/vite/detectServerImports.ts +0 -53
  109. package/data/Generator/templates/react-app/vite.config.ts +0 -74
  110. package/data/Generator/templates/sdk/wasp/dev/index.ts +0 -19
  111. package/data/packages/studio/dist/public/assets/Flow-b5112d3d.js +0 -26
  112. package/data/packages/studio/dist/public/assets/index-17ce6ed4.css +0 -1
  113. package/data/packages/studio/dist/public/assets/index-62a9d21a.js +0 -120
  114. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/FullPageWrapper.tsx +0 -0
  115. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.module.css +0 -0
  116. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.tsx +0 -0
  117. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Message.tsx +0 -0
  118. /package/data/Generator/templates/{react-app/src/test/vitest → sdk/wasp/client/test}/setup.ts +0 -0
@@ -0,0 +1,112 @@
1
+ {{={= =}=}}
2
+ import { type Plugin, type UserConfig } from 'vite'
3
+ import { resolve } from 'node:path'
4
+ import { readFile, access, constants } from 'node:fs/promises'
5
+ import { parse as parseDotenv } from 'dotenv'
6
+ import { expand, type DotenvPopulateInput } from 'dotenv-expand'
7
+
8
+ const envFileName = '{= clientEnvFileName =}'
9
+
10
+ export function envFile(): Plugin {
11
+ let envFilePath!: string
12
+ return {
13
+ name: 'wasp:env-file',
14
+ enforce: 'pre',
15
+ async config(config, env) {
16
+ const rootDir = config.root || process.cwd()
17
+ const envVars = await loadEnvVars({
18
+ rootDir,
19
+ // We are sure that `envPrefix` is defined because
20
+ // we defined it in an earlier plugin.
21
+ envPrefix: config.envPrefix!,
22
+ // We load the env file variables only in development,
23
+ // when building for production, users are expected to
24
+ // provide the environment variables inline.
25
+ loadDotEnvFile: env.command === 'serve',
26
+ })
27
+ envFilePath = resolve(rootDir, envFileName)
28
+
29
+ const prefixedVars = Object.entries(envVars)
30
+ .reduce((acc, [key, value]) => {
31
+ acc[`import.meta.env.${key}`] = JSON.stringify(value)
32
+ return acc
33
+ }, {} as Record<string, string>)
34
+
35
+ return {
36
+ // Disable Vite's default .env loading.
37
+ envDir: false,
38
+ define: prefixedVars,
39
+ }
40
+ },
41
+ configureServer(server) {
42
+ const reloadServerOnEnvFileEvent = (path: string) => {
43
+ if (path === envFilePath) {
44
+ server.restart()
45
+ }
46
+ }
47
+
48
+ server.watcher.on('add', reloadServerOnEnvFileEvent)
49
+ server.watcher.on('change', reloadServerOnEnvFileEvent)
50
+ server.watcher.on('unlink', reloadServerOnEnvFileEvent)
51
+ },
52
+ async buildStart() {
53
+ this.addWatchFile(envFilePath)
54
+ },
55
+ }
56
+ }
57
+
58
+ // Based on: https://github.com/vitejs/vite/blob/8bb32036792a6f522f5c947112f3d688add755a0/packages/vite/src/node/env.ts
59
+ export async function loadEnvVars({
60
+ rootDir,
61
+ envPrefix,
62
+ loadDotEnvFile,
63
+ }: {
64
+ rootDir: string
65
+ envPrefix: NonNullable<UserConfig['envPrefix']>
66
+ loadDotEnvFile: boolean
67
+ }): Promise<Record<string, string>> {
68
+ const envPrefixNormalized = Array.isArray(envPrefix) ? envPrefix : [envPrefix]
69
+ const env: Record<string, string> = {}
70
+
71
+ if (loadDotEnvFile) {
72
+ const envFilePath = resolve(rootDir, envFileName)
73
+ const parsed = await parseEnvFile(envFilePath)
74
+
75
+ // Let environment variables use each other. Make a copy of `process.env` so that `dotenv-expand`
76
+ // doesn't re-assign the expanded values to the global `process.env`.
77
+ const processEnv = { ...process.env } as DotenvPopulateInput
78
+ expand({ parsed, processEnv })
79
+
80
+ // Only keys that start with prefix are exposed to client.
81
+ for (const [key, value] of Object.entries(parsed)) {
82
+ if (envPrefixNormalized.some(prefix => key.startsWith(prefix))) {
83
+ env[key] = value
84
+ }
85
+ }
86
+ }
87
+
88
+ // Make sure that inline env variables are prioritized over env file variables.
89
+ // Follows the logic Vite uses for env variables.
90
+ for (const key in process.env) {
91
+ if (envPrefixNormalized.some(prefix => key.startsWith(prefix))) {
92
+ env[key] = process.env[key] as string
93
+ }
94
+ }
95
+
96
+ return env
97
+ }
98
+
99
+ async function parseEnvFile(envFilePath: string): Promise<Record<string, string>> {
100
+ try {
101
+ await access(envFilePath, constants.R_OK)
102
+ } catch {
103
+ return {}
104
+ }
105
+
106
+ try {
107
+ return parseDotenv(await readFile(envFilePath, 'utf-8'))
108
+ } catch (error) {
109
+ console.error(`Error parsing ${envFileName}:`, error)
110
+ throw error
111
+ }
112
+ }
@@ -0,0 +1,38 @@
1
+ import type { Plugin } from "vite";
2
+ import { getIndexHtmlContent } from "../../virtual-files/index.js";
3
+ import { makeVirtualFilesResolver, type VirtualFiles } from "../../virtual-files/resolver.js";
4
+
5
+ const INDEX_HTML_FILE_NAME = "index.html";
6
+
7
+ const resolveVirtualFiles = makeVirtualFilesResolver([
8
+ { id: INDEX_HTML_FILE_NAME, load: getIndexHtmlContent },
9
+ ]);
10
+
11
+ export function waspHtmlBuild(): Plugin {
12
+ let virtualFiles!: VirtualFiles;
13
+
14
+ return {
15
+ name: "wasp:html-build",
16
+ apply: "build",
17
+ config() {
18
+ return {
19
+ build: {
20
+ rollupOptions: {
21
+ // Vite tries to find the entry file on disk (which doesn't exist)
22
+ // so the build fails. We tell Vite/Rollup to use `index.html` even
23
+ // though it doesn't exist on the disk.
24
+ input: INDEX_HTML_FILE_NAME,
25
+ },
26
+ },
27
+ };
28
+ },
29
+ configResolved(config) {
30
+ virtualFiles = resolveVirtualFiles(config.root);
31
+ },
32
+ resolveId: (id) => virtualFiles.ids.get(id),
33
+ load(id) {
34
+ const loader = virtualFiles.loaders.get(id);
35
+ return loader?.();
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,35 @@
1
+ import type { Plugin } from "vite";
2
+ import { getIndexHtmlContent } from "../../virtual-files/index.js";
3
+
4
+ export function waspHtmlDev(): Plugin {
5
+ return {
6
+ name: "wasp:html-dev",
7
+ apply: "serve",
8
+ configureServer(server) {
9
+ return () => {
10
+ // Post middleware: runs after Vite's built-in SPA fallback
11
+ // middleware which resolves routes to `/index.html` which
12
+ // we pick up here and return the virtual `index.html` content.
13
+ server.middlewares.use(async (req, res, next) => {
14
+ if (req.url === "/" || req.url === `/index.html`) {
15
+ try {
16
+ const html = getIndexHtmlContent();
17
+ const transformedHtml = await server.transformIndexHtml(
18
+ req.url,
19
+ html
20
+ );
21
+
22
+ res.setHeader("Content-Type", "text/html");
23
+ res.end(transformedHtml);
24
+ return;
25
+ } catch (e) {
26
+ return next(e);
27
+ }
28
+ }
29
+
30
+ next();
31
+ });
32
+ };
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,32 @@
1
+ import { type Plugin } from 'vite'
2
+ import { spawn } from 'node:child_process'
3
+
4
+ export function typescriptCheck(): Plugin {
5
+ return {
6
+ name: 'wasp:typescript-check',
7
+ apply: 'build',
8
+ async buildStart() {
9
+ await runTsc()
10
+ },
11
+ }
12
+ }
13
+
14
+ function runTsc(): Promise<void> {
15
+ return new Promise((resolve, reject) => {
16
+ const child = spawn(
17
+ 'tsc',
18
+ ['--build', '--noEmit'],
19
+ {
20
+ stdio: 'inherit',
21
+ shell: process.platform === 'win32',
22
+ }
23
+ )
24
+
25
+ child.once('error', reject)
26
+ child.once('close', (code) =>
27
+ code === 0
28
+ ? resolve()
29
+ : reject(new Error(`TypeScript check failed (exit ${code})`))
30
+ )
31
+ })
32
+ }
@@ -1,20 +1,30 @@
1
- import { type Plugin, loadEnv } from 'vite'
1
+ import { type Plugin } from 'vite'
2
2
 
3
+ import { loadEnvVars } from './envFile.js'
3
4
  import {
4
5
  getValidatedEnvOrError,
5
6
  formatZodEnvErrors,
6
- } from 'wasp/env/validation'
7
- import { clientEnvSchema } from 'wasp/client/env/schema'
8
- import { getColorizedConsoleFormatString } from 'wasp/universal/ansiColors'
7
+ } from '../../../env/validation.js'
8
+ import { clientEnvSchema } from '../../env/schema.js'
9
+ import { getColorizedConsoleFormatString } from '../../../universal/ansiColors.js'
9
10
 
10
11
  const redColorFormatString = getColorizedConsoleFormatString('red');
11
12
 
12
13
  export function validateEnv(): Plugin {
13
14
  let validationResult: ReturnType<typeof getValidatedEnvOrError> | null = null
14
15
  return {
15
- name: 'wasp-validate-env',
16
- configResolved: (config) => {
17
- const env = loadEnv(config.mode, process.cwd(), config.envPrefix)
16
+ name: 'wasp:validate-env',
17
+ async configResolved(config) {
18
+ const env = await loadEnvVars({
19
+ rootDir: config.root,
20
+ // We are sure that `envPrefix` is defined because
21
+ // we defined it in an earlier plugin.
22
+ envPrefix: config.envPrefix!,
23
+ // We load the env file variables only in development,
24
+ // when building for production, users are expected to
25
+ // provide the environment variables inline.
26
+ loadDotEnvFile: config.command === 'serve',
27
+ })
18
28
  validationResult = getValidatedEnvOrError(env, clientEnvSchema)
19
29
 
20
30
  // Exit if we are in build mode, because we can't show the error in the browser.
@@ -0,0 +1,29 @@
1
+ {{={= =}=}}
2
+ import { type Plugin } from "vite";
3
+ import {
4
+ getIndexTsxContent,
5
+ getRoutesTsxContent,
6
+ } from "../virtual-files/index.js";
7
+ import { makeVirtualFilesResolver, type VirtualFiles } from "../virtual-files/resolver.js";
8
+
9
+ const resolveVirtualFiles = makeVirtualFilesResolver([
10
+ { id: "{= clientEntryPointPath =}", load: getIndexTsxContent },
11
+ { id: "{= routesEntryPointPath =}", load: getRoutesTsxContent },
12
+ ]);
13
+
14
+ export function waspVirtualModules(): Plugin {
15
+ let virtualFiles!: VirtualFiles;
16
+
17
+ return {
18
+ name: "wasp:virtual-modules",
19
+ enforce: "pre",
20
+ configResolved(config) {
21
+ virtualFiles = resolveVirtualFiles(config.root);
22
+ },
23
+ resolveId: (id) => virtualFiles.ids.get(id),
24
+ load(id) {
25
+ const loader = virtualFiles.loaders.get(id);
26
+ return loader?.();
27
+ },
28
+ };
29
+ }
@@ -0,0 +1,36 @@
1
+ import { type PluginOption } from "vite";
2
+ import react, { type Options as ReactOptions } from "@vitejs/plugin-react";
3
+ import { validateEnv } from "./validateEnv.js";
4
+ import { envFile } from "./envFile.js";
5
+ import { detectServerImports } from "./detectServerImports.js";
6
+ import { waspVirtualModules } from "./virtualModules.js";
7
+ import { waspHtmlDev } from "./html/dev.js";
8
+ import { waspHtmlBuild } from "./html/build.js";
9
+ import { typescriptCheck } from "./typescriptCheck.js";
10
+ import { waspConfig } from "./waspConfig.js";
11
+
12
+ export interface WaspPluginOptions {
13
+ reactOptions?: ReactOptions;
14
+ }
15
+
16
+ export function wasp(options?: WaspPluginOptions): PluginOption {
17
+ return [
18
+ /**
19
+ * Plugins running before core plugins (enforce: 'pre').
20
+ */
21
+ // The `wasp:config` plugin must come first because
22
+ // other plugins may depend on its configuration.
23
+ waspConfig(),
24
+ waspVirtualModules(),
25
+ envFile(),
26
+ detectServerImports(),
27
+ /**
28
+ * Plugins running after core Vite plugins.
29
+ */
30
+ typescriptCheck(),
31
+ waspHtmlDev(),
32
+ waspHtmlBuild(),
33
+ validateEnv(),
34
+ react(options?.reactOptions),
35
+ ];
36
+ }
@@ -0,0 +1,56 @@
1
+ {{={= =}=}}
2
+ import { type PluginOption, mergeConfig } from "vite";
3
+ import { defaultExclude } from "vitest/config"
4
+
5
+ export function waspConfig(): PluginOption {
6
+ return {
7
+ name: "wasp:config",
8
+ enforce: 'pre',
9
+ config(config) {
10
+ return mergeConfig({
11
+ base: "{= baseDir =}",
12
+ optimizeDeps: {
13
+ exclude: {=& depsExcludedFromOptimization =}
14
+ },
15
+ server: {
16
+ port: {= defaultClientPort =},
17
+ host: "0.0.0.0",
18
+ open: true,
19
+ },
20
+ envPrefix: "REACT_APP_",
21
+ build: {
22
+ outDir: "{= clientBuildDirPath =}",
23
+ },
24
+ resolve: {
25
+ // These packages rely on a single instance per page. Not deduping them
26
+ // causes runtime errors (e.g., hook rule violation in react, QueryClient
27
+ // instance error in react-query, Invariant Error in react-router).
28
+ dedupe: ["react", "react-dom", "@tanstack/react-query", "react-router"],
29
+ alias: [
30
+ {
31
+ // Vite doesn't look for `.prisma/client` imports in the `node_modules`
32
+ // folder. We point it to the correct place here.
33
+ // TODO: Check if we can remove when updating Prisma (#2504)
34
+ find: /^\.prisma\/client\/(.+)$/,
35
+ replacement: "node_modules/.prisma/client/$1.js",
36
+ },
37
+ {
38
+ // Handle bare .prisma/client import
39
+ find: /^\.prisma\/client$/,
40
+ replacement: "node_modules/.prisma/client",
41
+ },
42
+ ],
43
+ },
44
+ test: {
45
+ globals: true,
46
+ environment: "jsdom",
47
+ setupFiles: {=& vitest.setupFilesArray =},
48
+ exclude: [
49
+ ...defaultExclude,
50
+ "{= vitest.excludeWaspArtefactsPattern =}",
51
+ ]
52
+ },
53
+ }, config);
54
+ }
55
+ };
56
+ }
@@ -7,7 +7,6 @@
7
7
  name="viewport"
8
8
  content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
9
9
  />
10
- <link rel="manifest" href="/manifest.json" />
11
10
 
12
11
  {=& head =}
13
12
 
@@ -17,6 +16,6 @@
17
16
  <body>
18
17
  <noscript>You need to enable JavaScript to run this app.</noscript>
19
18
  <div id="root"></div>
20
- <script type="module" src="/src/index.tsx"></script>
19
+ <script type="module" src="{= clientEntryPointPath =}"></script>
21
20
  </body>
22
21
  </html>
@@ -0,0 +1,34 @@
1
+ {{={= =}=}}
2
+ // @ts-nocheck
3
+ import * as React from "react";
4
+ import * as ReactDOM from "react-dom/client";
5
+ import { getWaspApp } from "wasp/client/app";
6
+ {=!
7
+ // NOTE: We are not inlining routes mapping into this file becuase once we
8
+ // allow users to override the `index.tsx` entry point they can use the existing
9
+ // routes mapping.
10
+ =}
11
+ {=& routesMapping.importStatement =}
12
+
13
+ {=# rootComponent.isDefined =}
14
+ {=& rootComponent.importStatement =}
15
+ {=/ rootComponent.isDefined =}
16
+
17
+ {=# setupFn.isDefined =}
18
+ {=& setupFn.importStatement =}
19
+ {=/ setupFn.isDefined =}
20
+
21
+ {=# setupFn.isDefined =}
22
+ await {= setupFn.importIdentifier =}()
23
+ {=/ setupFn.isDefined =}
24
+
25
+ const app = getWaspApp({
26
+ {=# rootComponent.isDefined =}
27
+ rootElement: <{= rootComponent.importIdentifier =} />,
28
+ {=/ rootComponent.isDefined =}
29
+ routesMapping: {= routesMapping.importIdentifier =},
30
+ });
31
+
32
+ ReactDOM.createRoot(document.getElementById("root")!).render(
33
+ <React.StrictMode>{app}</React.StrictMode>,
34
+ );
@@ -0,0 +1,17 @@
1
+ {{={= =}=}}
2
+ // @ts-nocheck
3
+ {=# isAuthEnabled =}
4
+ import { createAuthRequiredPage } from "wasp/client/app"
5
+ {=/ isAuthEnabled =}
6
+
7
+ // These files are used from user-land and the import paths below are relative to the
8
+ // user's project dir, and not the SDK:
9
+ {=# pagesToImport =}
10
+ {=& importStatement =}
11
+ {=/ pagesToImport =}
12
+
13
+ export const routesMapping = {
14
+ {=# routes =}
15
+ {= name =}: {= targetComponent =},
16
+ {=/ routes =}
17
+ } as const;
@@ -0,0 +1,20 @@
1
+ import fs from "node:fs";
2
+
3
+ export function getIndexTsxContent(): string {
4
+ return getFileContentFromRelativePath("./files/index.tsx");
5
+ }
6
+
7
+ export function getRoutesTsxContent(): string {
8
+ return getFileContentFromRelativePath("./files/routes.tsx");
9
+ }
10
+
11
+ export function getIndexHtmlContent(): string {
12
+ return getFileContentFromRelativePath("./files/index.html");
13
+ }
14
+
15
+ function getFileContentFromRelativePath(relativePath: string): string {
16
+ return fs.readFileSync(
17
+ new URL(relativePath, import.meta.url),
18
+ "utf-8"
19
+ );
20
+ }
@@ -0,0 +1,28 @@
1
+ import * as path from "node:path";
2
+
3
+ export type VirtualFiles = {
4
+ ids: ReadonlyMap<string, string>;
5
+ loaders: ReadonlyMap<string, () => Promise<string> | string>;
6
+ };
7
+
8
+ type VirtualFilesDefinition = {
9
+ id: string;
10
+ load: () => Promise<string> | string;
11
+ };
12
+
13
+ export const makeVirtualFilesResolver =
14
+ (files: VirtualFilesDefinition[]) =>
15
+ (rootPath: string): VirtualFiles => {
16
+ const filesWithAbsPath = files.map((d) => ({
17
+ ...d,
18
+ absPath: path.resolve(rootPath, path.basename(d.id)),
19
+ }));
20
+
21
+ const ids = new Map(filesWithAbsPath.map((d) => [d.id, d.absPath]));
22
+
23
+ const loaders = new Map(
24
+ filesWithAbsPath.map((d) => [d.absPath, d.load]),
25
+ );
26
+
27
+ return { ids, loaders };
28
+ };
@@ -1,6 +1,6 @@
1
1
  {{={= =}=}}
2
2
  {
3
- "name": "wasp",
3
+ "name": "{= sdkPackageName =}",
4
4
  "version": "1.0.0",
5
5
  "private": true,
6
6
  "type": "module",
@@ -64,8 +64,6 @@
64
64
  {=! Used by our code, undocumented (but accessible) for users. =}
65
65
  "./server/auth/email": "./dist/server/auth/email/index.js",
66
66
  {=! Used by our code, undocumented (but accessible) for users. =}
67
- "./client/test/*": "./dist/client/test/*.js",
68
- {=! Used by our code, undocumented (but accessible) for users. =}
69
67
  "./server/crud/*": "./dist/server/crud/*.js",
70
68
  {=! Used by our code, undocumented (but accessible) for users. =}
71
69
  "./server/email/core/types": "./dist/server/email/core/types.js",
@@ -113,14 +111,18 @@
113
111
  "./server/operations": "./dist/server/operations/index.js",
114
112
  "./client/router": "./dist/client/router/index.js",
115
113
  "./client/test": "./dist/client/test/index.js",
114
+ "./client/test/setup": "./dist/client/test/setup.js",
116
115
  "./client/hooks": "./dist/client/hooks.js",
117
116
  "./client": "./dist/client/index.js",
118
- "./dev": "./dist/dev/index.js",
119
117
  "./env": "./dist/env/index.js",
120
118
  {=! Private: [client] =}
121
119
  "./env/validation": "./dist/env/validation.js",
122
120
  {=! Private: [client, sdk] =}
123
121
  "./client/env/schema": "./dist/client/env/schema.js",
122
+ {=! Private: [client] =}
123
+ "./client/app": "./dist/client/app/index.jsx",
124
+ {=! Private: [client] =}
125
+ "./client/vite": "./dist/client/vite/index.js",
124
126
 
125
127
  {=! todo(filip): Fixes below are for type errors in 0.13.1, remove ASAP =}
126
128
  {=! Used by our code (SDK for full-stack type safety), undocumented (but accessible) for users. =}
@@ -1,14 +1,74 @@
1
1
  import * as fs from "node:fs/promises"
2
2
  import * as path from "node:path"
3
3
 
4
- const base = path.resolve(import.meta.dirname, "..");
4
+ const base = path.resolve(import.meta.dirname, "..")
5
5
 
6
- const globs = ["./auth/forms/**/*.css"];
6
+ await copyAssets([
7
+ "./auth/forms/**/*.css",
8
+ "./client/app/components/**/*.css",
9
+ "./client/vite/virtual-files/files/**/*.*",
10
+ ])
7
11
 
8
- for await (const file of fs.glob(globs, { cwd: base })) {
9
- const src = path.join(base, file);
10
- const dest = path.join(base, "dist", file);
12
+ async function copyAssets(globs) {
13
+ const sourceFiles = await Array.fromAsync(fs.glob(globs, { cwd: base }))
14
+ const { copied, skipped } = await copyChangedFiles(sourceFiles)
15
+ console.log(`[copy-assets] ${copied} copied, ${skipped} skipped`)
16
+ }
17
+
18
+ async function copyChangedFiles(sourceFiles) {
19
+ let copied = 0
20
+ let skipped = 0
21
+
22
+ for (const file of sourceFiles) {
23
+ const sourcePath = getSourcePath(file)
24
+ const destPath = getDestinationPath(file)
25
+ try {
26
+ if (await shouldCopyFile(sourcePath, destPath)) {
27
+ await copyFile(sourcePath, destPath)
28
+ copied++
29
+ } else {
30
+ skipped++
31
+ }
32
+ } catch (error) {
33
+ console.error(`[copy-assets] Error processing ${file}:`, error.message)
34
+ throw error
35
+ }
36
+ }
37
+
38
+ return { copied, skipped }
39
+ }
40
+
41
+ /**
42
+ * File should be copied if:
43
+ * - It doesn't exist in the destination directory.
44
+ * - Its modification time is newer than the destination file.
45
+ *
46
+ * We want to avoid needless copying of files to avoid triggering
47
+ * Vite's file watcher which can cause full page reloads when not
48
+ * necessary.
49
+ */
50
+ async function shouldCopyFile(sourcePath, destPath) {
51
+ try {
52
+ const [sourceStat, destStat] = await Promise.all([
53
+ fs.stat(sourcePath),
54
+ fs.stat(destPath),
55
+ ])
56
+ return sourceStat.mtimeMs > destStat.mtimeMs
57
+ } catch {
58
+ // Most likely the destination doesn't exist, so we should copy the file.
59
+ return true
60
+ }
61
+ }
62
+
63
+ async function copyFile(sourcePath, destPath) {
64
+ await fs.mkdir(path.dirname(destPath), { recursive: true })
65
+ await fs.copyFile(sourcePath, destPath)
66
+ }
67
+
68
+ function getSourcePath(file) {
69
+ return path.join(base, file)
70
+ }
11
71
 
12
- await fs.mkdir(path.dirname(dest), { recursive: true });
13
- await fs.copyFile(src, dest);
72
+ function getDestinationPath(file) {
73
+ return path.join(base, "dist", file)
14
74
  }
@@ -184,5 +184,8 @@ export type OAuthData = {
184
184
  {=# enabledProviders.isKeycloakAuthEnabled =}
185
185
  | { providerName: 'keycloak'; tokens: import('arctic').KeycloakTokens }
186
186
  {=/ enabledProviders.isKeycloakAuthEnabled =}
187
+ {=# enabledProviders.isMicrosoftAuthEnabled =}
188
+ | { providerName: 'microsoft'; tokens: import('arctic').MicrosoftEntraIdTokens }
189
+ {=/ enabledProviders.isMicrosoftAuthEnabled =}
187
190
  | never
188
191
  )
@@ -22,6 +22,10 @@ export { github } from './providers/github.js';
22
22
  // PUBLIC API
23
23
  export { keycloak } from './providers/keycloak.js';
24
24
  {=/ enabledProviders.isKeycloakAuthEnabled =}
25
+ {=# enabledProviders.isMicrosoftAuthEnabled =}
26
+ // PUBLIC API
27
+ export { microsoft } from './providers/microsoft.js';
28
+ {=/ enabledProviders.isMicrosoftAuthEnabled =}
25
29
 
26
30
  // PRIVATE API
27
31
  export {
@@ -0,0 +1,23 @@
1
+ {{={= =}=}}
2
+ import { MicrosoftEntraId } from 'arctic';
3
+
4
+ import { getRedirectUriForCallback } from '../redirect.js';
5
+ import { defineProvider } from '../provider.js';
6
+ import { env } from '../../../env.js';
7
+
8
+ const id = '{= providerId =}';
9
+ const displayName = '{= displayName =}';
10
+
11
+ const oAuthClient = new MicrosoftEntraId(
12
+ env.MICROSOFT_TENANT_ID,
13
+ env.MICROSOFT_CLIENT_ID,
14
+ env.MICROSOFT_CLIENT_SECRET,
15
+ getRedirectUriForCallback(id).toString(),
16
+ );
17
+
18
+ // PUBLIC API
19
+ export const microsoft = defineProvider({
20
+ id,
21
+ displayName,
22
+ oAuthClient,
23
+ });