@webjsdev/cli 0.10.17 → 0.10.19

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.
@@ -1,168 +0,0 @@
1
- /**
2
- * Prisma-client preflight for `webjs dev` (#452).
3
- *
4
- * The scaffold's `dev` npm script is `webjs dev`, and `npm run dev` runs the
5
- * `predev` hook (`prisma generate`) FIRST. Invoking the `webjs dev` binary
6
- * directly (easy to do, and tempting for an AI/CLI) skips `predev`, so the dev
7
- * server boots against an ungenerated `@prisma/client` and crashes with a raw
8
- * "did not initialize yet" error and no hint that the canonical command is
9
- * `npm run dev`. This turns that crash into a one-line, actionable message.
10
- *
11
- * Scope is deliberately narrow: it only fires for an app that actually uses
12
- * Prisma (a `prisma/schema.prisma` OR an `@prisma/client` dependency), and it
13
- * only HINTS. It never auto-runs an arbitrary `predev` script and never shells
14
- * out to `prisma generate` on its own, keeping the no-build promise intact.
15
- *
16
- * Detection (verified against a real Prisma 6 install): the GENERATED
17
- * `.prisma/client` target is resolved through standard Node resolution from the
18
- * app (so a hoisted monorepo, where the client lives at a PARENT `node_modules`,
19
- * resolves correctly), then read. An ABSENT target, or a present-but-stub target
20
- * (the ungenerated client whose `PrismaClient` constructor throws the init
21
- * error), is "ungenerated". A real generated target older than the schema is
22
- * "stale". We do NOT grep the static `@prisma/client` re-export shim: it is
23
- * present in both states and never carries the init-error string itself.
24
- */
25
- import { existsSync, statSync, readFileSync } from 'node:fs';
26
- import { join, dirname } from 'node:path';
27
- import { createRequire } from 'node:module';
28
-
29
- /**
30
- * Does this app use Prisma? True if a schema is checked in OR `@prisma/client`
31
- * is a declared dependency. Either alone is enough; a non-Prisma app has
32
- * neither and gets no warning.
33
- *
34
- * @param {string} cwd
35
- * @returns {boolean}
36
- */
37
- export function usesPrisma(cwd) {
38
- if (existsSync(join(cwd, 'prisma', 'schema.prisma'))) return true;
39
- try {
40
- const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'));
41
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
42
- return Boolean(deps && deps['@prisma/client']);
43
- } catch {
44
- return false;
45
- }
46
- }
47
-
48
- // Marker the ungenerated `prisma-client-js` stub embeds in its generated target
49
- // (`node_modules/.prisma/client/index.js`). Verified against a real Prisma 6
50
- // install: after `npm i @prisma/client` but before `prisma generate`, the
51
- // generated `.prisma/client` entry IS present but its `PrismaClient` constructor
52
- // throws `@prisma/client did not initialize yet. Please run "prisma generate"`.
53
- // A real `prisma generate` replaces that stub with the generated client, which
54
- // does NOT contain this string. So the marker, read from the GENERATED target
55
- // (not the static `@prisma/client` shim), is the reliable ungenerated signal.
56
- const UNGENERATED_MARKER = 'did not initialize yet';
57
-
58
- /**
59
- * Resolve the GENERATED Prisma client entry (`.prisma/client/index.js`) for an
60
- * app, following standard Node resolution so a hoisted monorepo layout (the
61
- * generated client at a PARENT `node_modules`, the app under `apps/<x>`) still
62
- * resolves. Returns a discriminated result so the caller can tell the three
63
- * cases apart:
64
- * - `{ kind: 'unresolved' }` - `@prisma/client` itself is not resolvable.
65
- * - `{ kind: 'no-target' }` - the package resolves but `.prisma/client`
66
- * does not (a custom `output`, ambiguous).
67
- * - `{ kind: 'target', path }` - the generated target resolves.
68
- *
69
- * @param {string} cwd
70
- * @returns {{ kind: 'unresolved' } | { kind: 'no-target' } | { kind: 'target', path: string }}
71
- */
72
- function resolveGeneratedClient(cwd) {
73
- let clientDir;
74
- try {
75
- // Resolve @prisma/client AS THE APP would (hoisting-aware), then locate its
76
- // package dir. The shim itself loads `.prisma/client/default` relative to
77
- // here, so resolving from this dir follows the same (possibly hoisted) path.
78
- const appRequire = createRequire(join(cwd, 'noop.js'));
79
- clientDir = dirname(appRequire.resolve('@prisma/client'));
80
- } catch {
81
- return { kind: 'unresolved' };
82
- }
83
- const shimRequire = createRequire(join(clientDir, 'noop.js'));
84
- for (const entry of ['.prisma/client/index.js', '.prisma/client/default.js']) {
85
- try {
86
- return { kind: 'target', path: shimRequire.resolve(entry) };
87
- } catch { /* try the next entry */ }
88
- }
89
- return { kind: 'no-target' };
90
- }
91
-
92
- /**
93
- * Inspect the generated Prisma client state for a Prisma app.
94
- *
95
- * Returns one of:
96
- * - `{ status: 'ok' }` - client generated and not older than the schema.
97
- * - `{ status: 'missing' }` - schema/dep present but no generated client.
98
- * - `{ status: 'stale' }` - client exists but the schema is newer than it.
99
- *
100
- * Detection resolves the GENERATED `.prisma/client` target through standard Node
101
- * resolution (so hoisted monorepos are handled) and reads it: an absent target,
102
- * or a present-but-stub target (the ungenerated `PrismaClient` that throws on
103
- * construction), is `missing`. A real generated client that is older than the
104
- * schema is `stale`. A custom-`output` generator whose target Node cannot
105
- * resolve falls back to `ok` rather than nag a working app (false positives are
106
- * worse than a missed hint here).
107
- *
108
- * @param {string} cwd
109
- * @returns {{ status: 'ok' | 'missing' | 'stale' }}
110
- */
111
- export function prismaClientState(cwd) {
112
- const resolved = resolveGeneratedClient(cwd);
113
-
114
- // @prisma/client not resolvable: the app declared the dep (usesPrisma gated
115
- // us here) but it is not installed/generated. That is the boot-crash case.
116
- if (resolved.kind === 'unresolved') return { status: 'missing' };
117
-
118
- // The package resolves but the default `.prisma/client` target does not: a
119
- // custom `output` whose location we cannot cheaply verify. Fall back to `ok`
120
- // rather than nag a working app (false positives are worse than a missed hint).
121
- if (resolved.kind === 'no-target') return { status: 'ok' };
122
-
123
- const generatedIndex = resolved.path;
124
-
125
- // The generated target exists. Is it still the ungenerated stub (its
126
- // PrismaClient constructor throws the init error)?
127
- try {
128
- const body = readFileSync(generatedIndex, 'utf8');
129
- if (body.includes(UNGENERATED_MARKER)) return { status: 'missing' };
130
- } catch { /* unreadable: fall through to the stale check, then ok */ }
131
-
132
- // Generated for real. Is it older than the schema (a stale client)?
133
- const schema = join(cwd, 'prisma', 'schema.prisma');
134
- try {
135
- if (existsSync(schema)) {
136
- const schemaMtime = statSync(schema).mtimeMs;
137
- const clientMtime = statSync(generatedIndex).mtimeMs;
138
- if (schemaMtime > clientMtime) return { status: 'stale' };
139
- }
140
- } catch { /* if we can't stat, treat as ok */ }
141
-
142
- return { status: 'ok' };
143
- }
144
-
145
- /**
146
- * Build the actionable hint for an ungenerated/stale client, or `null` when the
147
- * app is fine or does not use Prisma. The caller prints it (a warning, not a
148
- * hard exit) before booting the dev server.
149
- *
150
- * @param {string} cwd
151
- * @returns {string | null}
152
- */
153
- export function prismaDevHint(cwd) {
154
- if (!usesPrisma(cwd)) return null;
155
- const { status } = prismaClientState(cwd);
156
- if (status === 'ok') return null;
157
-
158
- const reason =
159
- status === 'stale'
160
- ? 'Your Prisma client looks stale (the schema changed since it was generated).'
161
- : 'Your Prisma client is not generated yet.';
162
- return (
163
- `webjs: ${reason}\n` +
164
- ` The dev server will crash on an ungenerated client. Fix it with either:\n` +
165
- ` npm run dev # canonical: runs \`prisma generate\` (predev) first\n` +
166
- ` webjs db generate # just regenerate the client, then re-run\n`
167
- );
168
- }