@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.
- package/dist/create-package/index.js +3 -1
- package/dist/create-package/shared.js +92 -22
- package/dist/create-package/variants/client.js +10 -5
- package/dist/create-package/variants/contract.js +9 -5
- package/dist/create-package/variants/docker.js +9 -5
- package/dist/create-package/variants/empty.js +12 -5
- package/dist/create-package/variants/fullstack.js +54 -10
- package/dist/create-package/variants/server.js +9 -5
- package/package.json +2 -3
|
@@ -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: '
|
|
40
|
-
moduleResolution: '
|
|
41
|
-
outDir: options
|
|
42
|
-
rootDir: options
|
|
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
|
|
53
|
-
exclude: options?.exclude
|
|
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
|
-
|
|
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
|
-
'
|
|
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(
|
|
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
|
|
333
|
+
const rootConfig = await findNearestTsconfig(targetDir);
|
|
279
334
|
if (rootConfig) {
|
|
280
335
|
const relative = path.relative(targetDir, rootConfig);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
336
|
+
const normalized = toPosixPath(relative || './tsconfig.base.json');
|
|
337
|
+
extendsPath = normalized.startsWith('.') ? normalized : `./${normalized}`;
|
|
284
338
|
}
|
|
285
339
|
}
|
|
286
|
-
|
|
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: ['
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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':
|
|
104
|
-
|
|
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
|
|
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.
|
|
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,
|