@mpgd/cli 0.3.2 → 0.4.1
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/index.js +349 -22
- package/package.json +14 -2
- package/templates/phaser-game/README.md +130 -6
- package/templates/phaser-game/agent/acceptance.md +7 -2
- package/templates/phaser-game/agent/brief.md +1 -1
- package/templates/phaser-game/agent/game-manifest.json +5 -1
- package/templates/phaser-game/apps/target-cloudflare-pages/package.json +28 -0
- package/templates/phaser-game/apps/target-cloudflare-pages/src/worker.ts +12 -0
- package/templates/phaser-game/apps/target-cloudflare-pages/tsconfig.json +8 -0
- package/templates/phaser-game/apps/target-cloudflare-pages/vite.config.ts +28 -0
- package/templates/phaser-game/apps/target-cloudflare-pages/wrangler.jsonc +28 -0
- package/templates/phaser-game/apps/target-devvit/README.md +1 -1
- package/templates/phaser-game/apps/target-devvit/package.json +1 -1
- package/templates/phaser-game/apps/target-devvit/src/server/index.ts +4 -4
- package/templates/phaser-game/index.html +2 -0
- package/templates/phaser-game/legal/privacy.html +78 -0
- package/templates/phaser-game/legal/support.html +66 -0
- package/templates/phaser-game/legal/terms.html +70 -0
- package/templates/phaser-game/mpgd.targets.json +6 -0
- package/templates/phaser-game/package.json +42 -12
- package/templates/phaser-game/pnpm-workspace.yaml +7 -0
- package/templates/phaser-game/public/icon.svg +12 -0
- package/templates/phaser-game/public/manifest.webmanifest +20 -0
- package/templates/phaser-game/src/assets/manifest.ts +15 -0
- package/templates/phaser-game/src/assets/marker.svg +12 -0
- package/templates/phaser-game/src/env.d.ts +1 -0
- package/templates/phaser-game/src/i18n/messages.ts +3 -0
- package/templates/phaser-game/src/main.ts +49 -0
- package/templates/phaser-game/src/platform/installPlatform.ts +1 -1
- package/templates/phaser-game/src/platform/runtimeDetector.ts +9 -1
- package/templates/phaser-game/src/runtime/gameContext.ts +6 -1
- package/templates/phaser-game/src/scenes/BootScene.ts +8 -0
- package/templates/phaser-game/src/scenes/LobbyScene.ts +18 -2
- package/templates/phaser-game/src/scenes/PlayScene.ts +3 -2
- package/templates/phaser-game/src/styles.css +25 -2
- package/templates/phaser-game/vite.config.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -11,12 +11,38 @@ const packageRoot = resolvePackageRoot(sourceDir);
|
|
|
11
11
|
const detectedKitRoot = resolveDefaultKitRoot();
|
|
12
12
|
const gameTemplateDir = path.resolve(packageRoot, 'templates/phaser-game');
|
|
13
13
|
const cliVersion = readPackageVersion(packageRoot);
|
|
14
|
-
const
|
|
14
|
+
const recommendedMatrixTargets = 'web,microsoft-store,ait,reddit';
|
|
15
15
|
const defaultDependencyVersion = `^${cliVersion}`;
|
|
16
|
+
const mpgdTemplateDependencyPackages = [
|
|
17
|
+
{ name: '@mpgd/adapter-ait', packageDir: 'adapters/ait' },
|
|
18
|
+
{ name: '@mpgd/adapter-browser', packageDir: 'adapters/browser' },
|
|
19
|
+
{ name: '@mpgd/adapter-capacitor', packageDir: 'adapters/capacitor' },
|
|
20
|
+
{ name: '@mpgd/adapter-devvit', packageDir: 'adapters/devvit' },
|
|
21
|
+
{ name: '@mpgd/analytics', packageDir: 'packages/analytics' },
|
|
22
|
+
{ name: '@mpgd/bridge', packageDir: 'packages/bridge' },
|
|
23
|
+
{ name: '@mpgd/catalog', packageDir: 'packages/catalog' },
|
|
24
|
+
{ name: '@mpgd/cli', packageDir: 'packages/cli' },
|
|
25
|
+
{ name: '@mpgd/game-services', packageDir: 'packages/game-services' },
|
|
26
|
+
{ name: '@mpgd/i18n', packageDir: 'packages/i18n' },
|
|
27
|
+
{ name: '@mpgd/phaser-assets', packageDir: 'packages/phaser-assets' },
|
|
28
|
+
{ name: '@mpgd/platform', packageDir: 'packages/platform' },
|
|
29
|
+
{ name: '@mpgd/target-config', packageDir: 'packages/target-config' },
|
|
30
|
+
];
|
|
31
|
+
const recommendedMatrixTargetOrder = ['web-preview', 'microsoft-store', 'ait', 'reddit'];
|
|
32
|
+
const allMatrixTargetOrder = [
|
|
33
|
+
'web-preview',
|
|
34
|
+
'microsoft-store',
|
|
35
|
+
'android',
|
|
36
|
+
'ios',
|
|
37
|
+
'ait',
|
|
38
|
+
'reddit',
|
|
39
|
+
];
|
|
16
40
|
const supportedBuildTargets = [
|
|
17
41
|
'browser',
|
|
18
42
|
'web',
|
|
19
43
|
'web-preview',
|
|
44
|
+
'microsoft-store',
|
|
45
|
+
'msstore',
|
|
20
46
|
'android',
|
|
21
47
|
'ios',
|
|
22
48
|
'ait',
|
|
@@ -30,6 +56,7 @@ export async function runMpgdCli(args) {
|
|
|
30
56
|
version: cliVersion,
|
|
31
57
|
subCommands: {
|
|
32
58
|
game: gameCommand,
|
|
59
|
+
legal: legalCommand,
|
|
33
60
|
target: targetCommand,
|
|
34
61
|
kit: kitCommand,
|
|
35
62
|
},
|
|
@@ -148,8 +175,7 @@ const gameCommand = defineI18n({
|
|
|
148
175
|
'dependency-version': {
|
|
149
176
|
type: 'string',
|
|
150
177
|
required: false,
|
|
151
|
-
|
|
152
|
-
description: 'Version range for @mpgd packages.',
|
|
178
|
+
description: 'Override version range for every @mpgd package.',
|
|
153
179
|
},
|
|
154
180
|
workspace: {
|
|
155
181
|
type: 'boolean',
|
|
@@ -174,13 +200,13 @@ const gameCommand = defineI18n({
|
|
|
174
200
|
const packageName = readOptionalString(ctx.values['package-name']);
|
|
175
201
|
const dependencyVersion = ctx.values.workspace === true
|
|
176
202
|
? 'workspace:*'
|
|
177
|
-
:
|
|
203
|
+
: readOptionalString(ctx.values['dependency-version']);
|
|
178
204
|
const kitPath = readGameCreateKitPath(ctx.values, ctx.values.workspace === true);
|
|
179
205
|
createGameApp({
|
|
180
206
|
directory,
|
|
181
207
|
...(title === undefined ? {} : { title }),
|
|
182
208
|
...(packageName === undefined ? {} : { packageName }),
|
|
183
|
-
dependencyVersion,
|
|
209
|
+
...(dependencyVersion === undefined ? {} : { dependencyVersion }),
|
|
184
210
|
workspace: ctx.values.workspace === true,
|
|
185
211
|
...(kitPath === undefined ? {} : { kitPath }),
|
|
186
212
|
dryRun: ctx.values['dry-run'] === true,
|
|
@@ -192,6 +218,55 @@ const gameCommand = defineI18n({
|
|
|
192
218
|
console.log('Use "mpgd game create <directory>".');
|
|
193
219
|
},
|
|
194
220
|
});
|
|
221
|
+
const defaultLegalDir = 'legal';
|
|
222
|
+
const defaultLegalOutDir = 'artifacts/legal-site';
|
|
223
|
+
const legalPageSlugs = ['privacy', 'support', 'terms'];
|
|
224
|
+
const legalPageFileList = legalPageSlugs.map((slug) => `${slug}.html`).join(', ');
|
|
225
|
+
const legalCommand = defineI18n({
|
|
226
|
+
name: 'legal',
|
|
227
|
+
description: 'Build static legal/support pages for store and release evidence.',
|
|
228
|
+
resource: commandResource({
|
|
229
|
+
en: 'Build static legal/support pages for store and release evidence.',
|
|
230
|
+
ko: '스토어와 출시 증빙용 정적 약관/지원 페이지를 빌드합니다.',
|
|
231
|
+
}),
|
|
232
|
+
subCommands: {
|
|
233
|
+
build: defineI18n({
|
|
234
|
+
name: 'build',
|
|
235
|
+
description: 'Build privacy, support, and terms HTML pages.',
|
|
236
|
+
resource: commandResource({
|
|
237
|
+
en: 'Build privacy, support, and terms HTML pages.',
|
|
238
|
+
ko: 'privacy, support, terms HTML 페이지를 빌드합니다.',
|
|
239
|
+
}, legalArgResources()),
|
|
240
|
+
args: legalArgs(),
|
|
241
|
+
run: (ctx) => {
|
|
242
|
+
const result = buildLegalSite({
|
|
243
|
+
...resolveLegalCommandOptions(ctx.values),
|
|
244
|
+
check: false,
|
|
245
|
+
});
|
|
246
|
+
console.log(`Built legal site: ${result.outDir}`);
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
check: defineI18n({
|
|
250
|
+
name: 'check',
|
|
251
|
+
description: 'Check generated legal pages are current.',
|
|
252
|
+
resource: commandResource({
|
|
253
|
+
en: 'Check generated legal pages are current.',
|
|
254
|
+
ko: '생성된 legal 페이지가 최신인지 확인합니다.',
|
|
255
|
+
}, legalArgResources()),
|
|
256
|
+
args: legalArgs(),
|
|
257
|
+
run: (ctx) => {
|
|
258
|
+
const result = buildLegalSite({
|
|
259
|
+
...resolveLegalCommandOptions(ctx.values),
|
|
260
|
+
check: true,
|
|
261
|
+
});
|
|
262
|
+
console.log(`Legal site checked: ${result.outDir}`);
|
|
263
|
+
},
|
|
264
|
+
}),
|
|
265
|
+
},
|
|
266
|
+
run: () => {
|
|
267
|
+
console.log('Use "mpgd legal build" or "mpgd legal check".');
|
|
268
|
+
},
|
|
269
|
+
});
|
|
195
270
|
const targetCommand = defineI18n({
|
|
196
271
|
name: 'target',
|
|
197
272
|
description: 'Build and smoke target artifacts with generated game target config.',
|
|
@@ -311,7 +386,6 @@ const targetCommand = defineI18n({
|
|
|
311
386
|
targets: {
|
|
312
387
|
type: 'string',
|
|
313
388
|
required: false,
|
|
314
|
-
default: defaultMatrixTargets,
|
|
315
389
|
description: 'Comma-separated target list, or all.',
|
|
316
390
|
},
|
|
317
391
|
profile: {
|
|
@@ -337,11 +411,12 @@ const targetCommand = defineI18n({
|
|
|
337
411
|
run: (ctx) => {
|
|
338
412
|
const aitVariant = readOptionalString(ctx.values['ait-variant']);
|
|
339
413
|
const iosVariant = readOptionalString(ctx.values['ios-variant']);
|
|
414
|
+
const env = createTargetCommandEnv(ctx.values);
|
|
340
415
|
runTargetMatrix({
|
|
341
416
|
action: 'build',
|
|
342
|
-
targets: parseTargetList(readOptionalString(ctx.values.targets)),
|
|
417
|
+
targets: parseTargetList(readOptionalString(ctx.values.targets), env),
|
|
343
418
|
profile: readOptionalString(ctx.values.profile) ?? 'production',
|
|
344
|
-
env
|
|
419
|
+
env,
|
|
345
420
|
...(aitVariant === undefined ? {} : { aitVariant }),
|
|
346
421
|
...(iosVariant === undefined ? {} : { iosVariant }),
|
|
347
422
|
});
|
|
@@ -358,16 +433,41 @@ const targetCommand = defineI18n({
|
|
|
358
433
|
targets: {
|
|
359
434
|
type: 'string',
|
|
360
435
|
required: false,
|
|
361
|
-
default: defaultMatrixTargets,
|
|
362
436
|
description: 'Comma-separated target list, or all.',
|
|
363
437
|
},
|
|
364
438
|
...targetConfigArgs(),
|
|
365
439
|
},
|
|
366
440
|
run: (ctx) => {
|
|
441
|
+
const env = createTargetCommandEnv(ctx.values);
|
|
367
442
|
runTargetMatrix({
|
|
368
443
|
action: 'smoke',
|
|
369
|
-
targets: parseTargetList(readOptionalString(ctx.values.targets)),
|
|
370
|
-
env
|
|
444
|
+
targets: parseTargetList(readOptionalString(ctx.values.targets), env),
|
|
445
|
+
env,
|
|
446
|
+
});
|
|
447
|
+
},
|
|
448
|
+
}),
|
|
449
|
+
doctor: defineI18n({
|
|
450
|
+
name: 'doctor',
|
|
451
|
+
description: 'Verify existing target artifacts and game-owned output paths.',
|
|
452
|
+
resource: commandResource({
|
|
453
|
+
en: 'Verify existing target artifacts and game-owned output paths.',
|
|
454
|
+
ko: '기존 타깃 산출물과 게임 소유 출력 경로를 검증합니다.',
|
|
455
|
+
}, matrixArgResources()),
|
|
456
|
+
args: {
|
|
457
|
+
targets: {
|
|
458
|
+
type: 'string',
|
|
459
|
+
required: false,
|
|
460
|
+
default: 'all',
|
|
461
|
+
description: 'Comma-separated target list, or all.',
|
|
462
|
+
},
|
|
463
|
+
...targetConfigArgs(),
|
|
464
|
+
},
|
|
465
|
+
run: (ctx) => {
|
|
466
|
+
const env = createTargetCommandEnv(ctx.values);
|
|
467
|
+
runTargetMatrix({
|
|
468
|
+
action: 'smoke',
|
|
469
|
+
targets: parseTargetList(readOptionalString(ctx.values.targets) ?? 'all', env),
|
|
470
|
+
env,
|
|
371
471
|
});
|
|
372
472
|
},
|
|
373
473
|
}),
|
|
@@ -470,20 +570,146 @@ function matrixArgResources() {
|
|
|
470
570
|
},
|
|
471
571
|
};
|
|
472
572
|
}
|
|
573
|
+
function legalArgs() {
|
|
574
|
+
return {
|
|
575
|
+
'legal-dir': {
|
|
576
|
+
type: 'string',
|
|
577
|
+
required: false,
|
|
578
|
+
default: defaultLegalDir,
|
|
579
|
+
description: `Directory containing ${legalPageFileList}.`,
|
|
580
|
+
},
|
|
581
|
+
'out-dir': {
|
|
582
|
+
type: 'string',
|
|
583
|
+
required: false,
|
|
584
|
+
default: defaultLegalOutDir,
|
|
585
|
+
description: 'Output directory for the static legal site.',
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
function legalArgResources() {
|
|
590
|
+
return {
|
|
591
|
+
'legal-dir': {
|
|
592
|
+
en: `Directory containing ${legalPageFileList}.`,
|
|
593
|
+
ko: `${legalPageFileList} 이 있는 디렉터리.`,
|
|
594
|
+
},
|
|
595
|
+
'out-dir': {
|
|
596
|
+
en: 'Output directory for the static legal site.',
|
|
597
|
+
ko: '정적 legal 사이트 출력 디렉터리.',
|
|
598
|
+
},
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function resolveLegalCommandOptions(values) {
|
|
602
|
+
return {
|
|
603
|
+
legalDir: readOptionalString(values['legal-dir']) ?? defaultLegalDir,
|
|
604
|
+
outDir: readOptionalString(values['out-dir']) ?? defaultLegalOutDir,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function buildLegalSite(input) {
|
|
608
|
+
const legalDir = path.resolve(process.cwd(), input.legalDir);
|
|
609
|
+
const outDir = path.resolve(process.cwd(), input.outDir);
|
|
610
|
+
const outputs = createLegalSiteOutputs(legalDir, outDir);
|
|
611
|
+
if (input.check) {
|
|
612
|
+
const stale = outputs.filter((output) => {
|
|
613
|
+
if (!existsSync(output.file)) {
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
return readFileSync(output.file, 'utf8') !== output.content;
|
|
617
|
+
});
|
|
618
|
+
if (stale.length > 0) {
|
|
619
|
+
throw new Error([
|
|
620
|
+
'Legal site output is stale. Run "mpgd legal build".',
|
|
621
|
+
...stale.map((output) => `- ${path.relative(process.cwd(), output.file)}`),
|
|
622
|
+
].join('\n'));
|
|
623
|
+
}
|
|
624
|
+
return {
|
|
625
|
+
outDir,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
for (const output of outputs) {
|
|
629
|
+
mkdirSync(path.dirname(output.file), { recursive: true });
|
|
630
|
+
writeFileSync(output.file, output.content);
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
outDir,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function createLegalSiteOutputs(legalDir, outDir) {
|
|
637
|
+
const relativeLegalDir = path.relative(process.cwd(), legalDir);
|
|
638
|
+
const relativeOutDir = path.relative(process.cwd(), outDir);
|
|
639
|
+
if (relativeLegalDir.startsWith('..') || path.isAbsolute(relativeLegalDir)) {
|
|
640
|
+
throw new Error(`legal-dir must be inside the project root: ${legalDir}`);
|
|
641
|
+
}
|
|
642
|
+
if (relativeOutDir.startsWith('..') || path.isAbsolute(relativeOutDir)) {
|
|
643
|
+
throw new Error(`out-dir must be inside the project root: ${outDir}`);
|
|
644
|
+
}
|
|
645
|
+
const sourceRoot = toTemplatePath(relativeLegalDir || '.');
|
|
646
|
+
const outputs = legalPageSlugs.map((slug) => {
|
|
647
|
+
const sourceFile = path.join(legalDir, `${slug}.html`);
|
|
648
|
+
if (!existsSync(sourceFile)) {
|
|
649
|
+
throw new Error(`Missing legal page source: ${sourceFile}`);
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
file: path.join(outDir, slug, 'index.html'),
|
|
653
|
+
content: normalizeLegalHtml(readFileSync(sourceFile, 'utf8'), sourceFile),
|
|
654
|
+
};
|
|
655
|
+
});
|
|
656
|
+
return [
|
|
657
|
+
...outputs,
|
|
658
|
+
{
|
|
659
|
+
file: path.join(outDir, '_headers'),
|
|
660
|
+
content: [
|
|
661
|
+
'/*',
|
|
662
|
+
' X-Content-Type-Options: nosniff',
|
|
663
|
+
' Referrer-Policy: strict-origin-when-cross-origin',
|
|
664
|
+
' Cache-Control: public, max-age=300',
|
|
665
|
+
'',
|
|
666
|
+
].join('\n'),
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
file: path.join(outDir, '_redirects'),
|
|
670
|
+
content: [
|
|
671
|
+
'/ /support/ 302',
|
|
672
|
+
'/privacy /privacy/ 301',
|
|
673
|
+
'/support /support/ 301',
|
|
674
|
+
'/terms /terms/ 301',
|
|
675
|
+
'',
|
|
676
|
+
].join('\n'),
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
file: path.join(outDir, 'legal-site.json'),
|
|
680
|
+
content: `${JSON.stringify({
|
|
681
|
+
version: 1,
|
|
682
|
+
pages: legalPageSlugs.map((slug) => ({
|
|
683
|
+
slug,
|
|
684
|
+
path: `/${slug}/`,
|
|
685
|
+
source: `${sourceRoot}/${slug}.html`,
|
|
686
|
+
})),
|
|
687
|
+
}, null, 2)}\n`,
|
|
688
|
+
},
|
|
689
|
+
];
|
|
690
|
+
}
|
|
691
|
+
function normalizeLegalHtml(content, sourceFile) {
|
|
692
|
+
if (!/^<!doctype html>/iu.test(content) || !/<html[\s>]/iu.test(content)) {
|
|
693
|
+
throw new Error(`${sourceFile} must be a complete HTML document.`);
|
|
694
|
+
}
|
|
695
|
+
return content.endsWith('\n') ? content : `${content}\n`;
|
|
696
|
+
}
|
|
473
697
|
function createGameApp(input) {
|
|
474
698
|
const appDir = resolveAppDirectory(input.directory);
|
|
475
699
|
const gameName = path.basename(appDir);
|
|
476
700
|
assertValidGameName(gameName);
|
|
477
701
|
const packageName = input.packageName ?? gameName;
|
|
478
702
|
assertValidPackageName(packageName);
|
|
479
|
-
|
|
703
|
+
if (input.dependencyVersion !== undefined) {
|
|
704
|
+
assertValidDependencyVersion(input.dependencyVersion);
|
|
705
|
+
}
|
|
480
706
|
if (existsSync(appDir)) {
|
|
481
707
|
throw new Error(`Game directory already exists: ${appDir}`);
|
|
482
708
|
}
|
|
483
709
|
const context = createTemplateContext({
|
|
484
710
|
gameName,
|
|
485
711
|
packageName,
|
|
486
|
-
dependencyVersion: input.dependencyVersion,
|
|
712
|
+
...(input.dependencyVersion === undefined ? {} : { dependencyVersion: input.dependencyVersion }),
|
|
487
713
|
appDir,
|
|
488
714
|
workspace: input.workspace,
|
|
489
715
|
...(input.kitPath === undefined ? {} : { kitPath: input.kitPath }),
|
|
@@ -505,13 +731,13 @@ function createGameApp(input) {
|
|
|
505
731
|
console.log(`Created ${appDir}`);
|
|
506
732
|
console.log('Next steps:');
|
|
507
733
|
console.log(` cd ${appDir}`);
|
|
508
|
-
console.log(' pnpm install
|
|
734
|
+
console.log(' pnpm install -w');
|
|
509
735
|
console.log(' pnpm dev');
|
|
510
736
|
console.log('Target builds require an mpgd-kit checkout:');
|
|
511
737
|
console.log([
|
|
512
738
|
' mpgd target build-all',
|
|
513
739
|
`--targets-file ${path.join(appDir, 'mpgd.targets.json')}`,
|
|
514
|
-
`--targets ${
|
|
740
|
+
`--targets ${recommendedMatrixTargets}`,
|
|
515
741
|
'--ait-variant wrapper',
|
|
516
742
|
'--kit-path <path-to-mpgd-kit>',
|
|
517
743
|
].join(' '));
|
|
@@ -535,10 +761,13 @@ function assertValidPackageName(name) {
|
|
|
535
761
|
}
|
|
536
762
|
}
|
|
537
763
|
function assertValidDependencyVersion(version) {
|
|
538
|
-
if (
|
|
764
|
+
if (!isValidDependencyVersion(version)) {
|
|
539
765
|
throw new Error('Dependency version must be workspace:* or a semver version/range such as ^0.1.0.');
|
|
540
766
|
}
|
|
541
767
|
}
|
|
768
|
+
function isValidDependencyVersion(version) {
|
|
769
|
+
return /^(?:workspace:\*|[~^]?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.test(version);
|
|
770
|
+
}
|
|
542
771
|
function createTemplateContext(input) {
|
|
543
772
|
const title = input.title === undefined || input.title.length === 0 ? input.gameName : input.title;
|
|
544
773
|
assertValidGameTitle(title);
|
|
@@ -553,8 +782,16 @@ function createTemplateContext(input) {
|
|
|
553
782
|
devvitAppName: toDevvitAppName(input.gameName),
|
|
554
783
|
gameTitle: title,
|
|
555
784
|
gameTitleTsLiteral: toJavaScriptStringLiteral(title),
|
|
785
|
+
legalLastUpdated: new Date().toISOString().slice(0, 10),
|
|
556
786
|
packageName: input.packageName,
|
|
557
|
-
|
|
787
|
+
defaultDependencyVersion: input.dependencyVersion ?? defaultDependencyVersion,
|
|
788
|
+
mpgdDependencyVersionReplacements: resolveMpgdDependencyVersionReplacements({
|
|
789
|
+
...(input.dependencyVersion === undefined
|
|
790
|
+
? {}
|
|
791
|
+
: { overrideVersion: input.dependencyVersion }),
|
|
792
|
+
workspace: input.workspace,
|
|
793
|
+
...(kitPath === undefined ? {} : { kitPath }),
|
|
794
|
+
}),
|
|
558
795
|
tsconfigExtendsLine: input.workspace
|
|
559
796
|
? ` "extends": "${workspacePrefix}/tsconfig.base.json",`
|
|
560
797
|
: '',
|
|
@@ -589,6 +826,43 @@ function createTemplateContext(input) {
|
|
|
589
826
|
camelName: toCamelCase(input.gameName),
|
|
590
827
|
};
|
|
591
828
|
}
|
|
829
|
+
function resolveMpgdDependencyVersionReplacements(input) {
|
|
830
|
+
const packageJson = readPackageJsonOrUndefined(packageRoot, { strictParse: true });
|
|
831
|
+
const kitRoot = input.kitPath ?? detectedKitRoot;
|
|
832
|
+
return mpgdTemplateDependencyPackages.map((dependency) => {
|
|
833
|
+
const version = input.overrideVersion
|
|
834
|
+
?? (input.workspace ? 'workspace:*' : undefined)
|
|
835
|
+
?? resolvePublishedTemplateDependencyVersion(dependency.name, packageJson)
|
|
836
|
+
?? resolveWorkspaceTemplateDependencyVersion(kitRoot, dependency.packageDir)
|
|
837
|
+
?? defaultDependencyVersion;
|
|
838
|
+
return {
|
|
839
|
+
placeholder: mpgdDependencyVersionPlaceholder(dependency.name),
|
|
840
|
+
version,
|
|
841
|
+
};
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
function resolvePublishedTemplateDependencyVersion(packageName, packageJson) {
|
|
845
|
+
if (packageName === '@mpgd/cli') {
|
|
846
|
+
return `^${cliVersion}`;
|
|
847
|
+
}
|
|
848
|
+
const version = packageJson?.devDependencies?.[packageName];
|
|
849
|
+
return version !== undefined && version !== 'workspace:*' && isValidDependencyVersion(version)
|
|
850
|
+
? version
|
|
851
|
+
: undefined;
|
|
852
|
+
}
|
|
853
|
+
function resolveWorkspaceTemplateDependencyVersion(kitRoot, packageDir) {
|
|
854
|
+
if (kitRoot === undefined) {
|
|
855
|
+
return undefined;
|
|
856
|
+
}
|
|
857
|
+
const version = readPackageVersion(path.join(kitRoot, packageDir));
|
|
858
|
+
return version === '0.0.0' ? undefined : `^${version}`;
|
|
859
|
+
}
|
|
860
|
+
function mpgdDependencyVersionPlaceholder(packageName) {
|
|
861
|
+
return `__MPGD_DEPENDENCY_VERSION_${packageName
|
|
862
|
+
.replace(/^@mpgd\//, '')
|
|
863
|
+
.replaceAll('-', '_')
|
|
864
|
+
.toUpperCase()}__`;
|
|
865
|
+
}
|
|
592
866
|
function assertValidGameTitle(title) {
|
|
593
867
|
if (title.trim().length === 0) {
|
|
594
868
|
throw new Error('Game title cannot be empty.');
|
|
@@ -617,13 +891,14 @@ function walkTemplateDir(templateDir, relativeDir, files) {
|
|
|
617
891
|
}
|
|
618
892
|
}
|
|
619
893
|
function renderTemplate(content, context) {
|
|
620
|
-
|
|
894
|
+
let rendered = content
|
|
621
895
|
.replaceAll('__GAME_NAME__', context.gameName)
|
|
622
896
|
.replaceAll('__DEVVIT_APP_NAME__', context.devvitAppName)
|
|
623
897
|
.replaceAll('__GAME_TITLE_TS_LITERAL__', context.gameTitleTsLiteral)
|
|
624
898
|
.replaceAll('__GAME_TITLE__', context.gameTitle)
|
|
899
|
+
.replaceAll('__LEGAL_LAST_UPDATED__', context.legalLastUpdated)
|
|
625
900
|
.replaceAll('__PACKAGE_NAME__', context.packageName)
|
|
626
|
-
.replaceAll('__MPGD_DEPENDENCY_VERSION__', context.
|
|
901
|
+
.replaceAll('__MPGD_DEPENDENCY_VERSION__', context.defaultDependencyVersion)
|
|
627
902
|
.replaceAll('__TSCONFIG_EXTENDS_LINE__', context.tsconfigExtendsLine)
|
|
628
903
|
.replaceAll('__TSCONFIG_WORKSPACE_INCLUDES__', context.tsconfigWorkspaceIncludes)
|
|
629
904
|
.replaceAll('__TSCONFIG_WORKSPACE_EXCLUDES__', context.tsconfigWorkspaceExcludes)
|
|
@@ -632,6 +907,10 @@ function renderTemplate(content, context) {
|
|
|
632
907
|
.replaceAll('__PNPM_WORKSPACE_KIT_PACKAGES__', context.pnpmWorkspaceKitPackages)
|
|
633
908
|
.replaceAll('__PASCAL_NAME__', context.pascalName)
|
|
634
909
|
.replaceAll('__CAMEL_NAME__', context.camelName);
|
|
910
|
+
for (const replacement of context.mpgdDependencyVersionReplacements) {
|
|
911
|
+
rendered = rendered.replaceAll(replacement.placeholder, replacement.version);
|
|
912
|
+
}
|
|
913
|
+
return rendered;
|
|
635
914
|
}
|
|
636
915
|
function templateOutputPath(relativePath) {
|
|
637
916
|
return path.basename(relativePath) === 'gitignore'
|
|
@@ -792,10 +1071,13 @@ function withTargetVariantEnv(target, variant, env) {
|
|
|
792
1071
|
}
|
|
793
1072
|
return env;
|
|
794
1073
|
}
|
|
795
|
-
function parseTargetList(value) {
|
|
796
|
-
const raw = value ??
|
|
1074
|
+
function parseTargetList(value, env) {
|
|
1075
|
+
const raw = (value ?? 'default').trim();
|
|
797
1076
|
if (raw === 'all') {
|
|
798
|
-
return
|
|
1077
|
+
return configuredTargetsForOrder(env, allMatrixTargetOrder, 'all');
|
|
1078
|
+
}
|
|
1079
|
+
if (raw === 'default') {
|
|
1080
|
+
return configuredTargetsForOrder(env, recommendedMatrixTargetOrder, 'default');
|
|
799
1081
|
}
|
|
800
1082
|
const targets = raw
|
|
801
1083
|
.split(',')
|
|
@@ -807,10 +1089,50 @@ function parseTargetList(value) {
|
|
|
807
1089
|
}
|
|
808
1090
|
return [...new Set(targets)];
|
|
809
1091
|
}
|
|
1092
|
+
function configuredTargetsForOrder(env, order, label) {
|
|
1093
|
+
const configuredTargets = readConfiguredBuildTargets(env);
|
|
1094
|
+
const targets = order.filter((target) => configuredTargets.has(target));
|
|
1095
|
+
if (targets.length === 0) {
|
|
1096
|
+
throw new Error(`No ${label} targets are configured in ${readConfiguredTargetsFile(env)}.`);
|
|
1097
|
+
}
|
|
1098
|
+
return targets;
|
|
1099
|
+
}
|
|
1100
|
+
function readConfiguredBuildTargets(env) {
|
|
1101
|
+
const file = readConfiguredTargetsFile(env);
|
|
1102
|
+
const parsed = readJsonForCli(file);
|
|
1103
|
+
assertJsonObject(parsed, `targets file ${file}`);
|
|
1104
|
+
assertJsonObject(parsed.targets, `targets in ${file}`);
|
|
1105
|
+
const targets = new Set();
|
|
1106
|
+
for (const target of Object.keys(parsed.targets)) {
|
|
1107
|
+
const normalized = normalizeConfiguredBuildTarget(target);
|
|
1108
|
+
if (normalized !== undefined) {
|
|
1109
|
+
targets.add(normalized);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return targets;
|
|
1113
|
+
}
|
|
1114
|
+
function readConfiguredTargetsFile(env) {
|
|
1115
|
+
const file = env.MPGD_PLATFORM_TARGETS_FILE;
|
|
1116
|
+
if (file === undefined || file.length === 0) {
|
|
1117
|
+
throw new Error('Missing MPGD_PLATFORM_TARGETS_FILE.');
|
|
1118
|
+
}
|
|
1119
|
+
return file;
|
|
1120
|
+
}
|
|
1121
|
+
function normalizeConfiguredBuildTarget(target) {
|
|
1122
|
+
try {
|
|
1123
|
+
return normalizeBuildTarget(target);
|
|
1124
|
+
}
|
|
1125
|
+
catch {
|
|
1126
|
+
return undefined;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
810
1129
|
function normalizeBuildTarget(target) {
|
|
811
1130
|
if (target === 'browser' || target === 'web') {
|
|
812
1131
|
return 'web-preview';
|
|
813
1132
|
}
|
|
1133
|
+
if (target === 'msstore') {
|
|
1134
|
+
return 'microsoft-store';
|
|
1135
|
+
}
|
|
814
1136
|
if (target === 'devvit') {
|
|
815
1137
|
return 'reddit';
|
|
816
1138
|
}
|
|
@@ -819,6 +1141,11 @@ function normalizeBuildTarget(target) {
|
|
|
819
1141
|
}
|
|
820
1142
|
return target;
|
|
821
1143
|
}
|
|
1144
|
+
function assertJsonObject(input, label) {
|
|
1145
|
+
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
1146
|
+
throw new Error(`${label} must be an object.`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
822
1149
|
function runPnpm(args, commandEnv) {
|
|
823
1150
|
const kitPath = commandEnv.MPGD_KIT_PATH;
|
|
824
1151
|
if (kitPath === undefined || kitPath.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Gunshi CLI for mpgd-kit starter generation and target workflow orchestration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -48,7 +48,19 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/node": "^24.0.0",
|
|
50
50
|
"ttsc": "0.16.9",
|
|
51
|
-
"typescript": "7.0.1-rc"
|
|
51
|
+
"typescript": "7.0.1-rc",
|
|
52
|
+
"@mpgd/adapter-ait": "0.3.3",
|
|
53
|
+
"@mpgd/adapter-browser": "0.3.2",
|
|
54
|
+
"@mpgd/adapter-capacitor": "0.3.3",
|
|
55
|
+
"@mpgd/analytics": "0.3.2",
|
|
56
|
+
"@mpgd/adapter-devvit": "0.3.3",
|
|
57
|
+
"@mpgd/bridge": "0.4.0",
|
|
58
|
+
"@mpgd/catalog": "0.3.2",
|
|
59
|
+
"@mpgd/phaser-assets": "0.4.0",
|
|
60
|
+
"@mpgd/platform": "0.3.2",
|
|
61
|
+
"@mpgd/target-config": "0.3.4",
|
|
62
|
+
"@mpgd/i18n": "0.3.3",
|
|
63
|
+
"@mpgd/game-services": "0.3.3"
|
|
52
64
|
},
|
|
53
65
|
"main": "./dist/index.js",
|
|
54
66
|
"types": "./dist/index.d.ts",
|