create-vitnode-app 2.0.0-canary.2 → 2.0.0-canary.4

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 (32) hide show
  1. package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +1 -19
  2. package/copy-of-vitnode-app/api-single-app/src/routes/api/$.ts +1 -22
  3. package/copy-of-vitnode-app/api-single-app/src/server/api-bridge.ts +1 -12
  4. package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +1 -9
  5. package/copy-of-vitnode-app/monorepo/apps/web/.env.example +0 -11
  6. package/copy-of-vitnode-app/root/.env.example +0 -7
  7. package/copy-of-vitnode-app/root/global.d.ts +1 -19
  8. package/copy-of-vitnode-app/root/src/components/admin-shell.tsx +1 -44
  9. package/copy-of-vitnode-app/root/src/components/main-header.tsx +1 -30
  10. package/copy-of-vitnode-app/root/src/lib/admin-nav.ts +1 -38
  11. package/copy-of-vitnode-app/root/src/lib/admin-search.ts +1 -26
  12. package/copy-of-vitnode-app/root/src/lib/content-registry.ts +1 -42
  13. package/copy-of-vitnode-app/root/src/lib/i18n/runtime.ts +4 -53
  14. package/copy-of-vitnode-app/root/src/lib/i18n/shared.ts +1 -9
  15. package/copy-of-vitnode-app/root/src/lib/navigation.ts +1 -17
  16. package/copy-of-vitnode-app/root/src/lib/page-head.ts +1 -14
  17. package/copy-of-vitnode-app/root/src/locales/app.ts +1 -25
  18. package/copy-of-vitnode-app/root/src/locales/packages.ts +1 -34
  19. package/copy-of-vitnode-app/root/src/router.tsx +6 -160
  20. package/copy-of-vitnode-app/root/src/routes/__root.tsx +1 -98
  21. package/copy-of-vitnode-app/root/src/routes/_admin/admin.core.index.tsx +2 -51
  22. package/copy-of-vitnode-app/root/src/routes/_admin.tsx +6 -184
  23. package/copy-of-vitnode-app/root/src/routes/_main/index.tsx +0 -30
  24. package/copy-of-vitnode-app/root/src/routes/_main.tsx +1 -43
  25. package/copy-of-vitnode-app/root/src/server/messages.server.ts +0 -13
  26. package/copy-of-vitnode-app/root/src/start.ts +0 -19
  27. package/copy-of-vitnode-app/root/src/vitnode.config.ts +0 -56
  28. package/copy-of-vitnode-app/root/src/vitnode.server.config.ts +0 -11
  29. package/copy-of-vitnode-app/root/vite.config.ts +0 -84
  30. package/dist/src/plugin/create/route-templates.js +3 -40
  31. package/dist/tsconfig.build.tsbuildinfo +1 -1
  32. package/package.json +2 -2
@@ -7,49 +7,7 @@ import {
7
7
 
8
8
  import { MainHeader } from "#/components/main-header";
9
9
 
10
- /**
11
- * The main application shell - the header, the breadcrumb area and the one
12
- * `<main>` landmark that every public page renders inside.
13
- *
14
- * Pathless, so it contributes no URL segment: `/discover` is `/discover`, not
15
- * `/_main/discover`. A page joins the shell by *where its file lives*, which is
16
- * the same rule `_authenticated` uses for the session guard - and the reason
17
- * `_authenticated` lives underneath this one: a signed-in page is still a page
18
- * on the public site, so it wants the shell *and* the guard rather than a second
19
- * copy of the shell.
20
- *
21
- * ## What is deliberately outside it
22
- *
23
- * The four auth screens: `/login`, `/login/sso/$providerId`, `/register` and
24
- * `/login/reset-password`. An auth screen is a full-height card on an otherwise
25
- * empty document, and the header it would render is a header whose only
26
- * interesting control is "sign in". Keeping them out is what makes this a shell
27
- * that routes opt into rather than one every route is subject to.
28
- * `routes/api/$` is outside for a different reason: it is a server route and
29
- * renders no document at all. Note this is a visual difference from the Next.js
30
- * app, where all four sit inside `(main)` and do render the header.
31
- *
32
- * `src/tests/main-shell.test.ts` asserts both halves - the settings paths
33
- * inside, the four auth screens outside.
34
- *
35
- * ## The slots
36
- *
37
- * `ThemeLayoutContent`'s, and two of the same three the Next.js `ThemeLayout`
38
- * fills: `header` and `breadcrumb`.
39
- *
40
- * `listeners` is deliberately left empty here. The Next.js app puts the
41
- * notification toasts and the WebSocket's sign-in resync in it because its
42
- * `/login` is inside the main shell; this app's is not, so a sync mounted here
43
- * would not exist during the sign-in it has to notice. They are mounted by
44
- * `__root` instead, next to the connection whose lifetime they share.
45
- *
46
- * ## What it is not
47
- *
48
- * A provider. Every technical provider this app has - the QueryClient, the two
49
- * intl records, the theme, the WebSocket - is mounted once by `__root`, above
50
- * every route, because a login screen needs them just as much as a page under
51
- * this shell does. What lives here is structure: markup, and where the slots go.
52
- */
10
+
53
11
  export const Route = createFileRoute("/_main")({
54
12
  loader: async ({ context }) => await loadMainShell(context),
55
13
  component: MainLayout,
@@ -5,17 +5,4 @@ import { vitNodeServerConfig } from "#/vitnode.server.config";
5
5
 
6
6
  export type { IntlMessages } from "@vitnode/core/tanstack/i18n/server";
7
7
 
8
- /**
9
- * The messages one page needs, in one language - this app's loader.
10
- *
11
- * Everything about *how* messages load is core's: the source order (core, then
12
- * each plugin, then this app's overrides, later ones winning), the `web` scope
13
- * stamped on each so the API's tree in this same process cannot be served in its
14
- * place, the per-key fallback to the default locale, and the namespace pick that
15
- * keeps every plugin's AdminCP copy out of a page that renders none of it.
16
- *
17
- * What is this app's is the server config it is handed - the plugins it
18
- * registered, the languages it declares, and the loaders that stand in for each
19
- * package's own locale barrel.
20
- */
21
8
  export const loadIntlMessages = createIntlMessagesLoader(vitNodeServerConfig);
@@ -2,23 +2,4 @@ import { createVitNodeStart } from "@vitnode/core/tanstack/start";
2
2
 
3
3
  import { vitNodeConfig } from "#/vitnode.config";
4
4
 
5
- /**
6
- * This app's Start instance.
7
- *
8
- * The whole request pipeline is VitNode's, and it is mandatory rather than a
9
- * default: CSRF protection on server functions, canonical locale redirects with
10
- * the remembered-locale cookie, and the `private, no-store` directive every
11
- * document carrying a dehydrated session needs.
12
- *
13
- * Add this app's own request middleware - a request id, a tracing span, a
14
- * maintenance gate - with `requestMiddleware`. It runs after all of the above,
15
- * which is the only safe place for it: a redirect ends the request, so anything
16
- * in front of the locale rule would run twice for every visitor arriving at a
17
- * non-canonical URL.
18
- *
19
- * export const startInstance = createVitNodeStart({
20
- * config: vitNodeConfig,
21
- * requestMiddleware: [myMiddleware],
22
- * })
23
- */
24
5
  export const startInstance = createVitNodeStart({ config: vitNodeConfig });
@@ -1,66 +1,10 @@
1
1
  import { buildConfig } from "@vitnode/core/vitnode.config";
2
2
 
3
- /**
4
- * This app's configuration - the one file every VitNode app edits first.
5
- *
6
- * **Browser-safe, and everything here has to stay that way.** Three very
7
- * different readers depend on it: `routes/__root.tsx` renders the document shell
8
- * from `metadata`, `theme` and `debug`; `lib/i18n/runtime.ts` derives the locale
9
- * routing from `i18n`; and Vite's own plugin registry loads this file with
10
- * `jiti` while it is still resolving its config, to find out which plugins to
11
- * generate route, navigation and content-registry imports for. So it is plain
12
- * data and plugin *identity* - never a `() => import(...)` message loader, never
13
- * a module that reaches a database. Anything like that goes in
14
- * `vitnode.server.config.ts`.
15
- *
16
- * ## Adding a language
17
- *
18
- * Add an entry to `locales`. Packages ship their own translations, so a new
19
- * locale needs nothing else: anything a package has not translated falls back to
20
- * `defaultLocale` key by key. Register the package's file for that language in
21
- * `src/locales/packages.ts`, and put your own rewording in
22
- * `src/locales/app.ts` - both of which `vitnode.server.config.ts` picks up.
23
- *
24
- * `vitnode i18n:create de Deutsch` does all of it for you.
25
- *
26
- * ## Adding a plugin
27
- *
28
- * With the plugin's own factory:
29
- *
30
- * import { blogPlugin } from '@acme/blog/config'
31
- *
32
- * plugins: [blogPlugin()]
33
- *
34
- * That is the whole registration - the factory carries the plugin's content
35
- * types, its AdminCP navigation and its translations. Register its locale files
36
- * in `src/locales/packages.ts` as well, which is what the message loader
37
- * actually reads.
38
- *
39
- * What the AdminCP renders comes back through `src/admin-nav.gen.ts` and
40
- * `src/content-registry.gen.ts` rather than out of this object: the build writes
41
- * one literal import per configured plugin, and `src/router.tsx` loads the
42
- * content registry behind a dynamic `import()`, so a content type's editing
43
- * screen arrives with the route that renders it.
44
- *
45
- * A plugin's *pages* need nothing in this file at all. It declares them in its
46
- * own `src/routes.ts`, and this app's Vite build compiles them into
47
- * `src/plugin-routes.gen.ts`, which `src/router.tsx` mounts under the shell the
48
- * plugin's `area` names. No page is ever copied into `src/routes`.
49
- *
50
- * `buildConfig` also registers this object process-wide, which is how core's own
51
- * route files find it without being handed it as a prop.
52
- */
53
3
  export const vitNodeConfig = buildConfig({
54
4
  debug: false,
55
5
  i18n: {
56
6
  defaultLocale: "en",
57
7
  locales: [{ code: "en", name: "English" }],
58
- /**
59
- * Explicit, because the app renders on a server: without one, `use-intl`
60
- * formats dates in whatever zone the server happens to run in and warns
61
- * that the client will disagree.
62
- */
63
- timeZone: "UTC",
64
8
  },
65
9
  metadata: {
66
10
  shortTitle: "VitNode",
@@ -5,17 +5,6 @@ import { appMessages } from "#/locales/app";
5
5
  import { packageMessages } from "#/locales/packages";
6
6
  import { vitNodeConfig } from "#/vitnode.config";
7
7
 
8
- /**
9
- * The half of this app's configuration a browser may never hold.
10
- *
11
- * Both entries are `() => import(...)` loaders that read JSON out of a package's
12
- * build output, which is exactly what `vitnode.config.ts` cannot carry - so they
13
- * live here, beside the shared config rather than duplicating any of it. The
14
- * `server-only` marker makes an accidental import from a component a build error
15
- * instead of a browser bundle with every plugin's AdminCP copy in it.
16
- *
17
- * `src/server/messages.server.ts` is the only reader.
18
- */
19
8
  export const vitNodeServerConfig = buildServerConfig({
20
9
  config: vitNodeConfig,
21
10
  messages: appMessages,
@@ -8,92 +8,8 @@ import { defineConfig } from "vite";
8
8
 
9
9
  const config = defineConfig({
10
10
  resolve: { tsconfigPaths: true },
11
- /**
12
- * A second dev server has to fail rather than quietly move.
13
- *
14
- * `tanstackStart()` runs the route generator and *writes*
15
- * `src/routeTree.gen.ts`. Two servers means two generators owning one file: if
16
- * their route lists ever differ - which is precisely what happens when one was
17
- * started before a route file existed - they overwrite each other forever, and
18
- * every write is a full page reload. Without `strictPort` the second `pnpm dev`
19
- * says "Port 3000 is in use, trying another one" and succeeds, so the fight
20
- * starts silently and looks like an inexplicable refresh loop on the first
21
- * server.
22
- */
23
11
  server: { strictPort: true },
24
- ssr: {
25
- /**
26
- * The VitNode API packages mounted at `/api/*`, kept out of the SSR pass.
27
- *
28
- * They are Node libraries rather than app source: `@vitnode/core` loads its
29
- * locale files with a runtime `import("./en.json", { with: { type: "json" } })`
30
- * relative to its own `dist`. Bundling them in this pass moves that chunk
31
- * and the JSON stops resolving, which fails the build outright. Left
32
- * external here, Nitro resolves them from the package itself.
33
- *
34
- * Every VitNode package the app installs belongs on this list - add a
35
- * plugin's package name here when you install one.
36
- *
37
- * This is also what decides the shape of `@vitnode/core/tanstack/*`, so it is
38
- * worth naming the consequence rather than leaving it to be rediscovered.
39
- * Externalised here, the package skips this pass entirely and Nitro's own
40
- * Rollup run inlines its `dist` afterwards - and nothing in that path runs
41
- * the TanStack Start compiler. The *client* build has no such gap: it inlines
42
- * the package, so the compiler transforms it there. So package code reaches
43
- * the browser compiled and the server un-compiled, which is exactly why the
44
- * package may declare `createIsomorphicFn` (its stub falls back to the
45
- * `.server()` branch, which is what a server wants) and may never declare
46
- * `createServerFn` (un-compiled, its handler resolves to `undefined` with no
47
- * error at all). `packages/vitnode/src/tanstack/boundary.test.ts` holds the
48
- * package to that.
49
- *
50
- * Removing `@vitnode/core` from this list to close the gap was measured and
51
- * does not work: the SSR pass then reaches the locale barrel above and the
52
- * build fails on `Could not resolve './en.json'`.
53
- *
54
- * ## `tslib` is here for an entirely different reason
55
- *
56
- * It is not a VitNode package and it is not a Node library. It is here
57
- * because bundling it is broken, in a way that only appears in a production
58
- * build and only once a page renders.
59
- *
60
- * The Radix scroll-lock stack that every modal in a VitNode page pulls in -
61
- * `react-remove-scroll`, `use-sidecar`, `aria-hidden` - does
62
- * `import { __extends } from "tslib"`. Under the `node` condition that
63
- * resolves to `tslib/modules/index.js`, a two-line ESM shim around the
64
- * **CommonJS** `tslib.js`:
65
- *
66
- * import tslib from "../tslib.js";
67
- * const { __extends, … } = tslib;
68
- *
69
- * `tslib.js` defines `__esModule` on its exports, so Rolldown's CJS interop
70
- * helper does not synthesise a `default` - while the call site it generated
71
- * still reads `.default`. Every page that renders a dialog then throws
72
- * `Cannot destructure property '__extends' of undefined`.
73
- *
74
- * Externalised, the file is never bundled: Nitro traces the package into the
75
- * output and Node loads it with Node's own interop, which is correct. An
76
- * alias to `tslib/tslib.es6.mjs` was tried first and is worse - it fixes
77
- * Vite's copy and not Nitro's, because Nitro re-resolves externals itself,
78
- * and the tracer then copies only the file the alias named.
79
- */
80
- external: ["@vitnode/core", "tslib"],
81
- },
82
12
  plugins: [
83
- /**
84
- * Environment handling, the dev server's dependency pre-bundling and the
85
- * plugin route registry, in the order they have to run.
86
- *
87
- * `appRoot` is `import.meta.dirname` because a Vite config is loaded with
88
- * the working directory set to wherever the command ran, which in a
89
- * monorepo is regularly the repository root.
90
- *
91
- * `clientEnv` is not passed: this app publishes nothing to the browser
92
- * beyond the two keys VitNode inlines for every install
93
- * (`NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_WEB_URL`). Anything named there is
94
- * compiled into JavaScript anyone can read, so a key is added only when
95
- * something in the browser genuinely reads it.
96
- */
97
13
  vitnode({ appRoot: import.meta.dirname }),
98
14
  devtools(),
99
15
  nitro({ rollupConfig: { external: [/^@sentry\//] } }),
@@ -31,30 +31,7 @@ export const routes = definePluginRoutes([
31
31
  };
32
32
  export const pluginRouteModuleTemplate = (pluginName) => `import { useTranslations } from "use-intl";
33
33
 
34
- /**
35
- * The page \`routes.ts\` declares.
36
- *
37
- * Keep it framework-neutral. This module is compiled into the package's own
38
- * \`dist\` and imported by whichever app installed the plugin, so anything from
39
- * a router or a host-bound i18n package pins the plugin to one kind of host.
40
- * \`use-intl\` - which is what VitNode itself renders through - and plain JSX are
41
- * pinned to neither.
42
- *
43
- * No \`<main>\`: the application shell owns the document's one \`main\` landmark,
44
- * and a page that renders a second gives a screen reader two to choose between.
45
- * A page owns its container - width, padding, vertical rhythm - and nothing
46
- * above it.
47
- *
48
- * To give this route a loader, page metadata or a breadcrumb, add a \`route\`
49
- * export beside the default one:
50
- *
51
- * import { definePluginRoute } from "@vitnode/core/routing";
52
- *
53
- * export const route = definePluginRoute({
54
- * load: ({ context, params }) => fetchThing(context.locale, params.id),
55
- * head: ({ loaderData }) => ({ title: loaderData?.title }),
56
- * });
57
- */
34
+
58
35
  const HomePage = () => {
59
36
  const t = useTranslations("${pluginName}");
60
37
 
@@ -83,11 +60,7 @@ export const pluginMessagesTemplate = (pluginName) => `${JSON.stringify({
83
60
  }, null, 2)}\n`;
84
61
  export const pluginMessagesBarrelTemplate = () => `import type { LocaleMessagesMap } from "@vitnode/core/lib/i18n/types";
85
62
 
86
- /**
87
- * Every language this plugin ships. Add a file next to this one and a line here
88
- * to add another; apps pick it up with no copy step, because they read this
89
- * package's own \`dist\` rather than a copy of it.
90
- */
63
+
91
64
  const messages: LocaleMessagesMap = {
92
65
  en: async () => await import("./en.json", { with: { type: "json" } }),
93
66
  };
@@ -109,17 +82,7 @@ export const pluginConfigTemplate = (pluginName) => `import { buildPlugin } from
109
82
  import messages from "./locales";
110
83
  import { routes } from "./routes";
111
84
 
112
- /**
113
- * This plugin, as an application registers it.
114
- *
115
- * \`pluginId\` is the package name, and that is not a convention - it is how this
116
- * plugin's route tree is imported (\`${pluginName}/routes\`) and how its messages
117
- * are namespaced. The two cannot drift because they are one string.
118
- *
119
- * Add this to an app's \`src/vitnode.config.ts\` \`plugins\` array. A plugin that
120
- * is installed but not listed there contributes nothing - no directory is ever
121
- * scanned - so this is the only switch.
122
- */
85
+
123
86
  export const ${pluginVariableName(pluginName)} = () =>
124
87
  buildPlugin({
125
88
  pluginId: "${pluginName}",