@emeryld/manager 0.4.5 → 0.4.6

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.
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { stdin as input } from 'node:process';
5
5
  import { askLine, promptSingleKey } from '../prompts.js';
6
6
  import { colors, logGlobal } from '../utils/log.js';
7
- import { SCRIPT_DESCRIPTIONS, workspaceRoot, } from './shared.js';
7
+ import { SCRIPT_DESCRIPTIONS, workspaceRoot, ensureWorkspaceToolingFiles, } from './shared.js';
8
8
  import { clientVariant } from './variants/client.js';
9
9
  import { contractVariant } from './variants/contract.js';
10
10
  import { dockerVariant } from './variants/docker.js';
@@ -383,6 +383,8 @@ async function gatherTarget(initial = {}) {
383
383
  export async function createRrrPackage(options = {}) {
384
384
  const target = await gatherTarget(options);
385
385
  logGlobal(`Creating ${target.variant.label} in ${path.relative(workspaceRoot, target.targetDir) || '.'}`, colors.green);
386
+ const toolingRoot = target.variant.id === 'rrr-fullstack' ? target.targetDir : workspaceRoot;
387
+ await ensureWorkspaceToolingFiles(toolingRoot);
386
388
  await target.variant.scaffold({
387
389
  targetDir: target.targetDir,
388
390
  pkgName: target.pkgName,
@@ -1,7 +1,11 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { access, mkdir, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import { promptYesNoAll } from '../prompts.js';
4
5
  export const workspaceRoot = process.cwd();
6
+ export function isWorkspaceRoot(dir) {
7
+ return path.resolve(dir) === path.resolve(workspaceRoot);
8
+ }
5
9
  function pathExists(target) {
6
10
  return access(target)
7
11
  .then(() => true)
@@ -31,26 +35,45 @@ export async function writeFileIfMissing(baseDir, relative, contents) {
31
35
  console.log(` created ${rel}`);
32
36
  return 'created';
33
37
  }
38
+ export async function writeFileWithPrompt(baseDir, relative, contents) {
39
+ const fullPath = path.join(baseDir, relative);
40
+ await mkdir(path.dirname(fullPath), { recursive: true });
41
+ const rel = path.relative(workspaceRoot, fullPath);
42
+ const exists = await pathExists(fullPath);
43
+ if (exists) {
44
+ const answer = await promptYesNoAll(`Overwrite existing ${rel}?`);
45
+ if (answer !== 'yes') {
46
+ console.log(` kept existing ${rel}`);
47
+ return 'skipped';
48
+ }
49
+ }
50
+ await writeFile(fullPath, contents, 'utf8');
51
+ console.log(` ${exists ? 'updated' : 'created'} ${rel}`);
52
+ return exists ? 'updated' : 'created';
53
+ }
34
54
  export function baseTsConfig(options) {
35
55
  const config = {
36
56
  ...(options?.extends ? { extends: options.extends } : {}),
37
57
  compilerOptions: {
38
58
  target: 'ES2020',
39
- module: 'NodeNext',
40
- moduleResolution: 'NodeNext',
41
- outDir: options?.outDir ?? 'dist',
42
- rootDir: options?.rootDir ?? 'src',
43
- declaration: true,
44
- sourceMap: true,
45
- strict: true,
46
- esModuleInterop: true,
47
- skipLibCheck: true,
59
+ module: 'ESNext',
60
+ moduleResolution: 'Bundler',
61
+ ...(options?.outDir ? { outDir: options.outDir } : {}),
62
+ ...(options?.rootDir ? { rootDir: options.rootDir } : {}),
63
+ declaration: options?.declaration ?? true,
64
+ sourceMap: options?.sourceMap ?? true,
65
+ strict: options?.strict ?? true,
66
+ esModuleInterop: options?.esModuleInterop ?? true,
67
+ skipLibCheck: options?.skipLibCheck ?? true,
68
+ resolveJsonModule: options?.resolveJsonModule ?? true,
69
+ forceConsistentCasingInFileNames: options?.forceConsistentCasingInFileNames ?? true,
70
+ baseUrl: options?.baseUrl ?? '.',
48
71
  lib: options?.lib,
49
72
  types: options?.types,
50
73
  jsx: options?.jsx,
51
74
  },
52
- include: options?.include ?? ['src/**/*'],
53
- exclude: options?.exclude ?? ['dist', 'node_modules'],
75
+ include: options?.include,
76
+ exclude: options?.exclude,
54
77
  };
55
78
  return `${JSON.stringify(config, null, 2)}\n`;
56
79
  }
@@ -145,7 +168,8 @@ const DEFAULT_GITIGNORE_ENTRIES = [
145
168
  export function gitignoreFrom(entries = DEFAULT_GITIGNORE_ENTRIES) {
146
169
  return entries.join('\n');
147
170
  }
148
- export function baseScripts(devCommand, extras) {
171
+ export function baseScripts(devCommand, extras, options) {
172
+ const includePrepare = options?.includePrepare ?? true;
149
173
  return {
150
174
  dev: devCommand,
151
175
  build: 'tsc -p tsconfig.json',
@@ -157,18 +181,36 @@ export function baseScripts(devCommand, extras) {
157
181
  'format:check': 'prettier . --check',
158
182
  clean: 'rimraf dist .turbo coverage',
159
183
  test: "node -e \"console.log('No tests yet')\"",
160
- prepare: 'husky install || true',
184
+ ...(includePrepare
185
+ ? { prepare: 'git rev-parse --is-inside-work-tree >/dev/null 2>&1 && husky || true' }
186
+ : {}),
161
187
  ...extras,
162
188
  };
163
189
  }
164
190
  export function basePackageFiles(options) {
165
191
  return {
166
- 'eslint.config.js': baseEslintConfig(options?.tsconfigPath),
192
+ '.gitignore': gitignoreFrom(options?.gitignoreEntries),
193
+ };
194
+ }
195
+ export async function ensureWorkspaceToolingFiles(baseDir, options) {
196
+ const defaultTsconfig = path.join(baseDir, 'tsconfig.json');
197
+ const resolvedRootConfig = options?.tsconfigPath
198
+ ? path.resolve(baseDir, options.tsconfigPath)
199
+ : await resolveRootTsconfig(baseDir);
200
+ const tsconfigPath = toPosixPath(path.relative(baseDir, resolvedRootConfig ?? defaultTsconfig) || './tsconfig.json');
201
+ const files = workspaceToolingFiles(tsconfigPath);
202
+ for (const [relative, contents] of Object.entries(files)) {
203
+ // eslint-disable-next-line no-await-in-loop
204
+ await writeFileWithPrompt(baseDir, relative, contents);
205
+ }
206
+ }
207
+ export function workspaceToolingFiles(tsconfigPath) {
208
+ return {
209
+ 'eslint.config.js': baseEslintConfig(tsconfigPath),
167
210
  'prettier.config.js': basePrettierConfig(),
168
211
  '.prettierignore': `${PRETTIER_IGNORE}\n`,
169
212
  '.vscode/settings.json': vscodeSettings(),
170
213
  '.husky/pre-commit': HUSKY_PRE_COMMIT,
171
- '.gitignore': gitignoreFrom(options?.gitignoreEntries),
172
214
  };
173
215
  }
174
216
  export const SCRIPT_DESCRIPTIONS = {
@@ -261,27 +303,55 @@ function readRootPackageManager() {
261
303
  return undefined;
262
304
  }
263
305
  }
264
- async function resolveRootTsconfig() {
306
+ async function resolveRootTsconfig(baseDir = workspaceRoot) {
265
307
  const candidates = ['tsconfig.base.json', 'tsconfig.json'];
266
308
  for (const candidate of candidates) {
267
- const fullPath = path.join(workspaceRoot, candidate);
309
+ const fullPath = path.join(baseDir, candidate);
268
310
  if (await pathExists(fullPath)) {
269
311
  return fullPath;
270
312
  }
271
313
  }
272
314
  return undefined;
273
315
  }
316
+ async function findNearestTsconfig(startDir) {
317
+ let current = path.resolve(startDir);
318
+ // eslint-disable-next-line no-constant-condition
319
+ while (true) {
320
+ const found = await resolveRootTsconfig(current);
321
+ if (found)
322
+ return found;
323
+ const parent = path.dirname(current);
324
+ if (parent === current)
325
+ return undefined;
326
+ current = parent;
327
+ }
328
+ }
274
329
  export async function packageTsConfig(targetDir, options) {
275
330
  const extendsFromRoot = options?.extendsFromRoot ?? true;
276
331
  let extendsPath;
277
332
  if (extendsFromRoot) {
278
- const rootConfig = await resolveRootTsconfig();
333
+ const rootConfig = await findNearestTsconfig(targetDir);
279
334
  if (rootConfig) {
280
335
  const relative = path.relative(targetDir, rootConfig);
281
- if (relative !== '') {
282
- extendsPath = toPosixPath(relative || './tsconfig.json');
283
- }
336
+ const normalized = toPosixPath(relative || './tsconfig.base.json');
337
+ extendsPath = normalized.startsWith('.') ? normalized : `./${normalized}`;
284
338
  }
285
339
  }
286
- return baseTsConfig({ ...options, extends: extendsPath });
340
+ const compilerOptions = stripUndefined({
341
+ rootDir: options?.rootDir ?? 'src',
342
+ outDir: options?.outDir ?? 'dist',
343
+ tsBuildInfoFile: 'dist/.tsbuildinfo',
344
+ jsx: options?.jsx,
345
+ types: options?.types,
346
+ lib: options?.lib,
347
+ esModuleInterop: options?.esModuleInterop ?? true,
348
+ allowSyntheticDefaultImports: options?.esModuleInterop ?? true,
349
+ skipLibCheck: options?.skipLibCheck ?? true,
350
+ });
351
+ const config = stripUndefined({
352
+ extends: extendsPath,
353
+ compilerOptions,
354
+ include: options?.include ?? ['src/**/*.ts'],
355
+ });
356
+ return `${JSON.stringify(config, null, 2)}\n`;
287
357
  }
@@ -1,4 +1,4 @@
1
- import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, writeFileIfMissing, } from '../shared.js';
1
+ import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, isWorkspaceRoot, writeFileIfMissing, } from '../shared.js';
2
2
  const CLIENT_SCRIPTS = [
3
3
  'dev',
4
4
  'build',
@@ -29,10 +29,12 @@ export const healthGet = routeClient.build(registry.byKey['GET /api/health'])
29
29
  export const healthPost = routeClient.build(registry.byKey['POST /api/health'])
30
30
  `;
31
31
  }
32
- export function clientPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLDER) {
32
+ export function clientPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLDER, options) {
33
33
  return basePackageJson({
34
34
  name,
35
- scripts: baseScripts('tsx watch src/index.ts'),
35
+ scripts: baseScripts('tsx watch src/index.ts', undefined, {
36
+ includePrepare: options?.includePrepare,
37
+ }),
36
38
  dependencies: {
37
39
  [contractName]: 'workspace:*',
38
40
  '@emeryld/rrroutes-client': '^2.5.3',
@@ -46,12 +48,15 @@ export function clientPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLD
46
48
  });
47
49
  }
48
50
  async function clientFiles(pkgName, contractImport, targetDir) {
51
+ const includePrepare = isWorkspaceRoot(targetDir);
49
52
  const tsconfig = await packageTsConfig(targetDir, {
53
+ include: ['src/**/*.ts', 'src/**/*.tsx'],
50
54
  lib: ['ES2020', 'DOM'],
51
- types: ['node'],
55
+ types: ['react', 'react-native'],
56
+ jsx: 'react-jsx',
52
57
  });
53
58
  return {
54
- 'package.json': clientPackageJson(pkgName, contractImport),
59
+ 'package.json': clientPackageJson(pkgName, contractImport, { includePrepare }),
55
60
  'tsconfig.json': tsconfig,
56
61
  ...basePackageFiles(),
57
62
  'src/index.ts': clientIndexTs(contractImport),
@@ -1,4 +1,4 @@
1
- import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, writeFileIfMissing, } from '../shared.js';
1
+ import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, isWorkspaceRoot, writeFileIfMissing, } from '../shared.js';
2
2
  const CONTRACT_SCRIPTS = [
3
3
  'dev',
4
4
  'build',
@@ -93,7 +93,7 @@ export const socketConfig = sockets.config
93
93
  export const socketEvents = sockets.events
94
94
  export type AppRegistry = typeof registry
95
95
  `;
96
- function contractPackageJson(name) {
96
+ function contractPackageJson(name, options) {
97
97
  return basePackageJson({
98
98
  name,
99
99
  private: false,
@@ -104,7 +104,7 @@ function contractPackageJson(name) {
104
104
  },
105
105
  },
106
106
  // ✅ Solution #2: dev continuously emits dist/*.js + dist/*.d.ts
107
- scripts: baseScripts('tsc -p tsconfig.json --watch --preserveWatchOutput'),
107
+ scripts: baseScripts('tsc -p tsconfig.json --watch --preserveWatchOutput', undefined, { includePrepare: options?.includePrepare }),
108
108
  // You can keep this pinned if you want, but the import strategy above prevents breakage
109
109
  // across different module export shapes.
110
110
  dependencies: {
@@ -117,9 +117,13 @@ function contractPackageJson(name) {
117
117
  });
118
118
  }
119
119
  async function contractFiles(pkgName, targetDir) {
120
- const tsconfig = await packageTsConfig(targetDir);
120
+ const includePrepare = isWorkspaceRoot(targetDir);
121
+ const tsconfig = await packageTsConfig(targetDir, {
122
+ include: ['src/**/*.ts', 'src/**/*.tsx'],
123
+ skipLibCheck: true,
124
+ });
121
125
  return {
122
- 'package.json': contractPackageJson(pkgName),
126
+ 'package.json': contractPackageJson(pkgName, { includePrepare }),
123
127
  'tsconfig.json': tsconfig,
124
128
  ...basePackageFiles(),
125
129
  'src/index.ts': CONTRACT_TS,
@@ -1,4 +1,4 @@
1
- import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, writeFileIfMissing, } from '../shared.js';
1
+ import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, isWorkspaceRoot, writeFileIfMissing, } from '../shared.js';
2
2
  const DOCKER_SCRIPTS = [
3
3
  'dev',
4
4
  'build',
@@ -18,7 +18,7 @@ const DOCKER_SCRIPTS = [
18
18
  'docker:clean',
19
19
  'docker:reset',
20
20
  ];
21
- function dockerPackageJson(name) {
21
+ function dockerPackageJson(name, options) {
22
22
  return basePackageJson({
23
23
  name,
24
24
  scripts: baseScripts('tsx watch src/index.ts', {
@@ -31,7 +31,7 @@ function dockerPackageJson(name) {
31
31
  'docker:stop': 'npm run docker:cli -- stop',
32
32
  'docker:clean': 'npm run docker:cli -- clean',
33
33
  'docker:reset': 'npm run docker:cli -- reset',
34
- }),
34
+ }, { includePrepare: options?.includePrepare }),
35
35
  dependencies: {
36
36
  cors: '^2.8.5',
37
37
  express: '^5.1.0',
@@ -96,9 +96,13 @@ CMD ["node", "dist/index.js"]
96
96
  `;
97
97
  }
98
98
  async function dockerFiles(pkgName, targetDir) {
99
- const tsconfig = await packageTsConfig(targetDir, { types: ['node'] });
99
+ const includePrepare = isWorkspaceRoot(targetDir);
100
+ const tsconfig = await packageTsConfig(targetDir, {
101
+ include: ['src/**/*.ts'],
102
+ types: ['node'],
103
+ });
100
104
  return {
101
- 'package.json': dockerPackageJson(pkgName),
105
+ 'package.json': dockerPackageJson(pkgName, { includePrepare }),
102
106
  'tsconfig.json': tsconfig,
103
107
  'src/index.ts': dockerIndexTs(),
104
108
  'scripts/docker.ts': dockerCliScript(pkgName),
@@ -1,4 +1,4 @@
1
- import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, writeFileIfMissing, } from '../shared.js';
1
+ import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, isWorkspaceRoot, writeFileIfMissing, } from '../shared.js';
2
2
  const EMPTY_SCRIPTS = [
3
3
  'dev',
4
4
  'build',
@@ -10,19 +10,26 @@ const EMPTY_SCRIPTS = [
10
10
  'clean',
11
11
  'test',
12
12
  ];
13
- function emptyPackageJson(name) {
13
+ function emptyPackageJson(name, options) {
14
14
  return basePackageJson({
15
15
  name,
16
- scripts: baseScripts('tsx watch src/index.ts'),
16
+ scripts: baseScripts('tsx watch src/index.ts', undefined, {
17
+ includePrepare: options?.includePrepare,
18
+ }),
17
19
  devDependencies: {
18
20
  ...BASE_LINT_DEV_DEPENDENCIES,
19
21
  },
20
22
  });
21
23
  }
22
24
  async function emptyFiles(pkgName, targetDir) {
23
- const tsconfig = await packageTsConfig(targetDir, { types: ['node'] });
25
+ const includePrepare = isWorkspaceRoot(targetDir);
26
+ const tsconfig = await packageTsConfig(targetDir, {
27
+ include: ['src/**/*.ts', 'src/**/*.tsx'],
28
+ types: ['node'],
29
+ skipLibCheck: true,
30
+ });
24
31
  return {
25
- 'package.json': emptyPackageJson(pkgName),
32
+ 'package.json': emptyPackageJson(pkgName, { includePrepare }),
26
33
  'tsconfig.json': tsconfig,
27
34
  ...basePackageFiles(),
28
35
  'src/index.ts': "export const hello = 'world'\n",
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, baseTsConfig, writeFileIfMissing, } from '../shared.js';
2
+ import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, writeFileIfMissing, } from '../shared.js';
3
3
  import { clientVariant } from './client.js';
4
4
  import { serverVariant } from './server.js';
5
5
  import { dockerVariant } from './docker.js';
@@ -43,6 +43,9 @@ function deriveDirs(rootDir, baseName) {
43
43
  docker: path.join(packagesRoot, `${baseName}-docker`),
44
44
  };
45
45
  }
46
+ function toPosixPath(value) {
47
+ return value.split(path.sep).join('/');
48
+ }
46
49
  function rootPackageJson(baseName) {
47
50
  const dockerPackageDir = `packages/${baseName}-docker`;
48
51
  return basePackageJson({
@@ -50,7 +53,7 @@ function rootPackageJson(baseName) {
50
53
  private: true,
51
54
  useDefaults: false,
52
55
  scripts: {
53
- setup: 'pnpm install && pnpm exec husky install',
56
+ setup: 'pnpm install && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && pnpm exec husky || true)',
54
57
  dev: 'pnpm -r dev --parallel --if-present',
55
58
  build: 'pnpm -r build',
56
59
  typecheck: 'pnpm -r typecheck',
@@ -61,7 +64,7 @@ function rootPackageJson(baseName) {
61
64
  'format:check': 'pnpm -r format:check --if-present',
62
65
  test: 'pnpm -r test --if-present',
63
66
  clean: 'pnpm -r clean --if-present && rimraf node_modules .turbo coverage',
64
- prepare: 'husky install || true',
67
+ prepare: 'git rev-parse --is-inside-work-tree >/dev/null 2>&1 && husky || true',
65
68
  'docker:up': `pnpm -C ${dockerPackageDir} run docker:up`,
66
69
  'docker:dev': `pnpm -C ${dockerPackageDir} run docker:dev`,
67
70
  'docker:logs': `pnpm -C ${dockerPackageDir} run docker:logs`,
@@ -77,6 +80,50 @@ function rootPackageJson(baseName) {
77
80
  function rootPnpmWorkspace() {
78
81
  return "packages:\n - 'packages/*'\n";
79
82
  }
83
+ function rootTsconfigBase(baseName, names) {
84
+ const paths = {
85
+ [names.contract]: [`packages/${baseName}-contract/src`],
86
+ [`${names.contract}/*`]: [`packages/${baseName}-contract/src/*`],
87
+ [names.server]: [`packages/${baseName}-server/src`],
88
+ [`${names.server}/*`]: [`packages/${baseName}-server/src/*`],
89
+ [names.client]: [`packages/${baseName}-client/src`],
90
+ [`${names.client}/*`]: [`packages/${baseName}-client/src/*`],
91
+ [names.docker]: [`packages/${baseName}-docker/src`],
92
+ [`${names.docker}/*`]: [`packages/${baseName}-docker/src/*`],
93
+ };
94
+ const config = {
95
+ $schema: 'https://json.schemastore.org/tsconfig',
96
+ compilerOptions: {
97
+ target: 'ES2020',
98
+ module: 'ESNext',
99
+ moduleResolution: 'Bundler',
100
+ jsx: 'react-jsx',
101
+ strict: true,
102
+ skipLibCheck: true,
103
+ resolveJsonModule: true,
104
+ forceConsistentCasingInFileNames: true,
105
+ sourceMap: true,
106
+ baseUrl: '.',
107
+ paths,
108
+ },
109
+ };
110
+ return `${JSON.stringify(config, null, 2)}\n`;
111
+ }
112
+ function rootSolutionTsconfig(dirs) {
113
+ const references = [
114
+ dirs.contract,
115
+ dirs.server,
116
+ dirs.client,
117
+ dirs.docker,
118
+ ].map((pkgDir) => ({ path: toPosixPath(path.relative(dirs.root, pkgDir)) }));
119
+ const config = {
120
+ $schema: 'https://json.schemastore.org/tsconfig',
121
+ extends: './tsconfig.base.json',
122
+ files: [],
123
+ references,
124
+ };
125
+ return `${JSON.stringify(config, null, 2)}\n`;
126
+ }
80
127
  function stackComposeYaml() {
81
128
  return `services:
82
129
  db:
@@ -95,16 +142,13 @@ volumes:
95
142
  db-data:
96
143
  `;
97
144
  }
98
- async function scaffoldRootFiles(baseDir, baseName) {
145
+ async function scaffoldRootFiles(baseDir, baseName, names, dirs) {
99
146
  const files = {
100
147
  'package.json': rootPackageJson(baseName),
101
148
  'pnpm-workspace.yaml': rootPnpmWorkspace(),
102
149
  'docker-compose.yml': stackComposeYaml(),
103
- 'tsconfig.json': baseTsConfig({
104
- rootDir: '.',
105
- outDir: 'dist',
106
- include: ['packages/**/*', 'scripts/**/*', 'src/**/*'],
107
- }),
150
+ 'tsconfig.base.json': rootTsconfigBase(baseName, names),
151
+ 'tsconfig.json': rootSolutionTsconfig(dirs),
108
152
  ...basePackageFiles(),
109
153
  };
110
154
  for (const [relative, contents] of Object.entries(files)) {
@@ -136,7 +180,7 @@ export const fullstackVariant = {
136
180
  const names = deriveNames(baseName);
137
181
  const dirs = deriveDirs(ctx.targetDir, baseName);
138
182
  // Root workspace files
139
- await scaffoldRootFiles(dirs.root, baseName);
183
+ await scaffoldRootFiles(dirs.root, baseName, names, dirs);
140
184
  // Contract package (reuse contract variant)
141
185
  await contractVariant.scaffold({
142
186
  targetDir: dirs.contract,
@@ -1,4 +1,4 @@
1
- import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, writeFileIfMissing, } from '../shared.js';
1
+ import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, packageTsConfig, isWorkspaceRoot, writeFileIfMissing, } from '../shared.js';
2
2
  const SERVER_SCRIPTS = [
3
3
  'dev',
4
4
  'build',
@@ -58,13 +58,13 @@ server.listen(PORT, () => {
58
58
  })
59
59
  `;
60
60
  }
61
- export function serverPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLDER) {
61
+ export function serverPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLDER, options) {
62
62
  return basePackageJson({
63
63
  name,
64
64
  private: false,
65
65
  scripts: baseScripts('tsx watch --env-file .env src/index.ts', {
66
66
  start: 'node dist/index.js',
67
- }),
67
+ }, { includePrepare: options?.includePrepare }),
68
68
  dependencies: {
69
69
  [contractName]: 'workspace:*',
70
70
  '@emeryld/rrroutes-server': '^2.4.1',
@@ -82,9 +82,13 @@ export function serverPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLD
82
82
  });
83
83
  }
84
84
  async function serverFiles(pkgName, contractImport, targetDir) {
85
- const tsconfig = await packageTsConfig(targetDir, { types: ['node'] });
85
+ const includePrepare = isWorkspaceRoot(targetDir);
86
+ const tsconfig = await packageTsConfig(targetDir, {
87
+ include: ['src/**/*.ts'],
88
+ types: ['node'],
89
+ });
86
90
  return {
87
- 'package.json': serverPackageJson(pkgName, contractImport),
91
+ 'package.json': serverPackageJson(pkgName, contractImport, { includePrepare }),
88
92
  'tsconfig.json': tsconfig,
89
93
  ...basePackageFiles(),
90
94
  'src/index.ts': serverIndexTs(contractImport),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emeryld/manager",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Interactive manager for pnpm monorepos (update/test/build/publish).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,8 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@types/node": "^20.17.0",
32
32
  "@types/semver": "^7.7.1",
33
- "cross-env": "^7.0.3",
34
- "@emeryld/manager": "^0.4.1"
33
+ "cross-env": "^7.0.3"
35
34
  },
36
35
  "ts-node": {
37
36
  "esm": true,