@@ -61,6 +61,36 @@ export function sanitizeNamespace(name: string): string {
6161 return s ;
6262}
6363
64+ /**
65+ * Native dependencies the scaffold pulls in (transitively) that need their
66+ * build scripts to run at install time. pnpm 10+ blocks dependency build
67+ * scripts by default; without this allowlist `better-sqlite3` (used by the
68+ * default standalone SQLite store, via knex) ships uncompiled and `serve`
69+ * fails with "Could not locate the bindings file".
70+ *
71+ * Current pnpm reads this from `pnpm-workspace.yaml`, NOT the `pnpm` field in
72+ * package.json (that field is now ignored and emits a deprecation warning).
73+ * npm/yarn/bun build native modules by default and ignore this file.
74+ */
75+ export const SCAFFOLD_BUILT_DEPENDENCIES = [ 'better-sqlite3' , 'esbuild' ] ;
76+
77+ /**
78+ * Render the `pnpm-workspace.yaml` that allowlists native build scripts.
79+ * Kept minimal (no `packages:` key) so it acts purely as a settings file for
80+ * the single-package scaffold rather than declaring a workspace.
81+ */
82+ export function renderPnpmWorkspaceYaml ( builtDeps : string [ ] = SCAFFOLD_BUILT_DEPENDENCIES ) : string {
83+ return [
84+ '# Allowlist native dependency build scripts so `pnpm install` compiles' ,
85+ '# them (pnpm 10+ blocks build scripts by default). Without this,' ,
86+ '# better-sqlite3 ships uncompiled and `objectstack serve` fails with' ,
87+ '# "Could not locate the bindings file".' ,
88+ 'onlyBuiltDependencies:' ,
89+ ...builtDeps . map ( ( d ) => ` - ${ d } ` ) ,
90+ '' ,
91+ ] . join ( '\n' ) ;
92+ }
93+
6494export const TEMPLATES : Record < string , {
6595 description : string ;
6696 dependencies : Record < string , string > ;
@@ -88,7 +118,10 @@ export const TEMPLATES: Record<string, {
88118 } ,
89119 scripts : {
90120 dev : 'objectstack dev' ,
91- start : 'objectstack serve' ,
121+ // `serve` is production mode and boots from the compiled artifact
122+ // (dist/objectstack.json). Compile first so `pnpm start` works straight
123+ // after `pnpm install` without a separate build step.
124+ start : 'objectstack compile && objectstack serve' ,
92125 build : 'objectstack compile' ,
93126 validate : 'objectstack validate' ,
94127 typecheck : 'tsc --noEmit' ,
@@ -404,6 +437,19 @@ export default class Init extends Command {
404437 printInfo ( 'package.json already exists, skipping' ) ;
405438 }
406439
440+ // 1b. Create pnpm-workspace.yaml so pnpm compiles the native deps the
441+ // standalone store needs (see SCAFFOLD_BUILT_DEPENDENCIES). Harmless for
442+ // npm/yarn/bun, which ignore this file and build native modules anyway.
443+ // Use an exclusive write ('wx') rather than exists()-then-write so the
444+ // "don't clobber an existing file" check is atomic (no TOCTOU race).
445+ const pnpmWorkspacePath = path . join ( targetDir , 'pnpm-workspace.yaml' ) ;
446+ try {
447+ fs . writeFileSync ( pnpmWorkspacePath , renderPnpmWorkspaceYaml ( ) , { flag : 'wx' } ) ;
448+ createdFiles . push ( 'pnpm-workspace.yaml' ) ;
449+ } catch ( err : any ) {
450+ if ( err ?. code !== 'EEXIST' ) throw err ;
451+ }
452+
407453 // 2. Create objectstack.config.ts
408454 const configContent = template . configContent ( projectName , namespace ) ;
409455 fs . writeFileSync ( path . join ( targetDir , 'objectstack.config.ts' ) , configContent ) ;
0 commit comments