create-spatius-app 0.1.3-beta.2 → 0.1.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.
@@ -1,5 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { spawn } from 'node:child_process';
3
+ import { existsSync } from 'node:fs';
3
4
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
5
  import { createRequire } from 'node:module';
5
6
  import { delimiter, dirname, join } from 'node:path';
@@ -27,7 +28,7 @@ const concurrentlyBin = join(
27
28
  require(concurrentlyPackage).bin.concurrently,
28
29
  );
29
30
 
30
- async function supervise(f, extraEnv) {
31
+ async function supervise(f, extraEnv, manager = 'npm', executable = manager) {
31
32
  const viteBin = join(f.root, 'node_modules', 'vite', 'bin');
32
33
  await mkdir(viteBin, { recursive: true });
33
34
  await writeFile(
@@ -37,14 +38,25 @@ async function supervise(f, extraEnv) {
37
38
  const sourcePackage = JSON.parse(
38
39
  await readFile(join(templateDirectory, 'package.json'), 'utf8'),
39
40
  );
40
- // Exercise the production startup gate and supervisor with npm, available alongside
41
- // Node in both generated projects and root CI. Only PM selection changes.
41
+ // npm is available alongside Node. The pnpm regression also exercises the
42
+ // package manager running this suite, without downloading another version.
42
43
  const scripts = Object.fromEntries(
43
44
  ['dev', 'dev:web', 'agent:dev'].map((name) => [
44
45
  name,
45
- sourcePackage.scripts[name].replaceAll('pnpm run ', 'npm run '),
46
+ sourcePackage.scripts[name].replaceAll('pnpm run ', `${manager} run `),
46
47
  ]),
47
48
  );
49
+ // Observe completed production cleanup independently of pipe closure. A package
50
+ // manager can exit early while descendants still hold its output pipes open.
51
+ const finished = join(f.root, 'dev-finished');
52
+ await writeFile(
53
+ join(f.root, 'scripts', 'dev-finished.mjs'),
54
+ `import { writeFileSync } from 'node:fs'; await import('./dev.mjs'); writeFileSync(${JSON.stringify(finished)}, '');`,
55
+ );
56
+ scripts.dev = scripts.dev.replace(
57
+ 'scripts/dev.mjs',
58
+ 'scripts/dev-finished.mjs',
59
+ );
48
60
  await writeFile(
49
61
  join(f.root, 'package.json'),
50
62
  JSON.stringify({
@@ -72,7 +84,7 @@ async function supervise(f, extraEnv) {
72
84
  );
73
85
  const env = { ...f.env, ...extraEnv };
74
86
  env.PATH += `${delimiter}${process.env.PATH ?? process.env.Path ?? ''}`;
75
- const spec = commandSpec('npm', ['run', 'dev'], env);
87
+ const spec = commandSpec(executable, ['run', 'dev'], env);
76
88
  const child = spawn(spec.command, spec.arguments_, {
77
89
  cwd: f.root,
78
90
  env,
@@ -92,7 +104,10 @@ async function supervise(f, extraEnv) {
92
104
  child.once('error', reject);
93
105
  child.once('close', (code, signal) => resolve({ code, signal, output }));
94
106
  });
95
- return { child, done };
107
+ const exited = new Promise((resolve) => {
108
+ child.once('exit', () => resolve({ finished: existsSync(finished) }));
109
+ });
110
+ return { child, done, exited };
96
111
  }
97
112
 
98
113
  for (const [name, side, code, probe] of [
@@ -183,6 +198,50 @@ test(
183
198
  },
184
199
  );
185
200
 
201
+ // Generated npm/bun projects need not install pnpm just to run their checks.
202
+ // Root CI and generated pnpm checks supply the exact invoking pnpm executable.
203
+ const pnpmExecutable = process.env.npm_execpath;
204
+ test(
205
+ 'pnpm waits for dev cleanup before exiting after one Ctrl+C',
206
+ {
207
+ timeout: 15000,
208
+ skip:
209
+ windows ||
210
+ !pnpmExecutable ||
211
+ !/pnpm(?:\.[cm]?js)?$/i.test(pnpmExecutable),
212
+ },
213
+ async (t) => {
214
+ const f = await fixture(t);
215
+ const run = await supervise(
216
+ f,
217
+ {
218
+ DEV_TEST_RUNTIME_HOLD: '1',
219
+ DEV_TEST_GRANDCHILD: '1',
220
+ DEV_TEST_DETACHED: '1',
221
+ },
222
+ 'pnpm',
223
+ pnpmExecutable,
224
+ );
225
+ const events = await waitForEvents(
226
+ f.events,
227
+ (events) =>
228
+ events.filter(
229
+ ({ role, type }) => role === 'grandchild' && type === 'ready',
230
+ ).length === 2,
231
+ );
232
+ await delay(250);
233
+ process.kill(-run.child.pid, 'SIGINT');
234
+ assert.equal(
235
+ (await run.exited).finished,
236
+ true,
237
+ 'pnpm returned before dev cleanup finished; use a pnpm launcher >=12.4.1 when using pnpm 12.',
238
+ );
239
+ const result = await run.done;
240
+ assertNoCredentials(result.output);
241
+ await assertStopped(events.map(({ pid }) => pid));
242
+ },
243
+ );
244
+
186
245
  test(
187
246
  'a timed-out CLI probe reaps its descendants before Python fallback, including Windows cmd wrappers',
188
247
  { timeout: 15000 },