@mpgd/cli 0.55.0 → 0.57.0

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.
@@ -5,7 +5,7 @@ import { spawnSync } from 'node:child_process';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { DOMParser } from '@xmldom/xmldom';
7
7
  import { satisfies, valid, validRange } from 'semver';
8
- import { capacitorGatewayRegistersNotifications, conflictingPushPlugin, notificationsCapacitorGatewayModules, notificationsCapacitorGateways, notificationsCapacitorSupportFiles, notificationsCapacitorTemplateSupportFiles, notificationsGameDependencies, notificationsMessagingFirebaseRange, notificationsMessagingPlugin, notificationsProviderId, notificationsShellPlugins, wireCapacitorGatewayNotifications, } from './notifications-starter.js';
8
+ import { capacitorGatewayRegistersNotifications, conflictingPushPlugin, notificationsCapacitorGatewayModules, notificationsCapacitorGateways, notificationsCapacitorSupportFiles, notificationsCapacitorTemplateSupportFiles, notificationsCapacitorTemplateSupportImporters, notificationsGameDependencies, notificationsMessagingFirebaseRange, notificationsMessagingPlugin, notificationsProviderId, notificationsShellPlugins, sourceReferencesRelativeModule, wireCapacitorGatewayNotifications, } from './notifications-starter.js';
9
9
  import { isNonPublicServiceHostname } from './production-target-readiness.js';
10
10
  import { assertAndroidSettingsAppProject, assertAndroidSettingsNoAppRemap, assertIosReleaseInfoPlistExpansion, assertIosReleasePackagingSettings, assertIosReleaseProductName, assertNativeShellIdentity, hasAndroidDisplayNameResourceOverride, hasAndroidManifestSourceSetOverride, hasAndroidResourceSourceSetOverride, hasGradleIdentityMutation, hasGradleTaskAction, maskGradleStrings, readIosAppTargetId, readIosReleaseInfoPlist, stripGradleComments, } from './native-shell-identity.js';
11
11
  const shellPath = 'apps/mobile-capacitor';
@@ -2796,9 +2796,29 @@ function planNotificationsProvider(input) {
2796
2796
  // Base-template modules they import, missing from a game an earlier release created.
2797
2797
  if (input.templateRoot !== undefined) {
2798
2798
  for (const file of notificationsCapacitorTemplateSupportFiles) {
2799
- if (readExisting(safeDestination(gameRoot, file)) === undefined) {
2800
- files.push({ path: file, content: starterFile(input.templateRoot, file) });
2799
+ if (readExisting(safeDestination(gameRoot, file)) !== undefined) {
2800
+ continue;
2801
2801
  }
2802
+ const imported = notificationsCapacitorTemplateSupportImporters[file];
2803
+ const importerSource = imported === undefined
2804
+ ? undefined
2805
+ : readExisting(safeDestination(gameRoot, imported.importer));
2806
+ // A game whose own importer does not import the module would never use it.
2807
+ if (imported !== undefined && importerSource !== undefined
2808
+ && !sourceReferencesRelativeModule(importerSource, path.posix.basename(file, '.ts'))) {
2809
+ warnings.push(imported.warning);
2810
+ continue;
2811
+ }
2812
+ // The file imports a kit export an older installed package lacks.
2813
+ const required = imported?.requires;
2814
+ if (required !== undefined
2815
+ && installedPackageLacksExport(gameRoot, required.packageName, required.exportName)) {
2816
+ const version = installedPackageVersion(gameRoot, required.packageName);
2817
+ throw new Error(`--providers notifications writes ${file}, which imports ${required.exportName} from `
2818
+ + `${required.packageName}; the game's ${required.packageName} ${version} does not export it. `
2819
+ + "Upgrade the game's kit packages (mpgd kit upgrade) first.");
2820
+ }
2821
+ files.push({ path: file, content: starterFile(input.templateRoot, file) });
2802
2822
  }
2803
2823
  }
2804
2824
  warnings.push('The notification settings screen is not added to an existing game; render one from '
@@ -2905,3 +2925,110 @@ function requireString(value, label) {
2905
2925
  function json(value) {
2906
2926
  return `${JSON.stringify(value, null, 2)}\n`;
2907
2927
  }
2928
+ /** Declaration files the export check reads at most: a guard against a pathological graph. */
2929
+ const maxDeclarationFilesChecked = 500;
2930
+ /** The declaration file names TypeScript pairs with a module specifier. */
2931
+ function declarationCandidates(base) {
2932
+ // A specifier that already names a declaration file is that file.
2933
+ if (/\.d\.[cm]?ts$/u.test(base)) {
2934
+ return [base];
2935
+ }
2936
+ const extension = path.extname(base);
2937
+ const stem = base.slice(0, base.length - extension.length);
2938
+ switch (extension) {
2939
+ case '.js':
2940
+ case '.ts':
2941
+ return [`${stem}.d.ts`];
2942
+ case '.mjs':
2943
+ case '.mts':
2944
+ return [`${stem}.d.mts`];
2945
+ case '.cjs':
2946
+ case '.cts':
2947
+ return [`${stem}.d.cts`];
2948
+ default:
2949
+ return [`${base}.d.ts`, path.join(base, 'index.d.ts')];
2950
+ }
2951
+ }
2952
+ /**
2953
+ * Whether the game's installed `packageName` provably lacks `exportName`:
2954
+ * its root type declarations, and every declaration file they re-export
2955
+ * from through relative specifiers, at any depth, never name it. Anything
2956
+ * the check cannot follow passes, and the game's build then decides, as for
2957
+ * the adapter's settings opener: an uninstalled or unreadable package, a
2958
+ * module it cannot read, or an `export *` from another package.
2959
+ */
2960
+ function installedPackageLacksExport(gameRoot, packageName, exportName) {
2961
+ // Only a plain identifier is searched for; anything else is not proof of absence.
2962
+ if (!/^[A-Za-z_$][\w$]*$/u.test(exportName)) {
2963
+ return false;
2964
+ }
2965
+ const packageRoot = path.join(gameRoot, 'node_modules', packageName);
2966
+ const manifest = readInstalledManifest(gameRoot, packageName);
2967
+ if (manifest === undefined) {
2968
+ return false;
2969
+ }
2970
+ const rootExport = typeof manifest.exports === 'object' && manifest.exports !== null
2971
+ ? manifest.exports['.']
2972
+ : undefined;
2973
+ const rootTypes = typeof rootExport === 'object' && rootExport !== null
2974
+ ? rootExport.types
2975
+ : undefined;
2976
+ const typesPath = typeof rootTypes === 'string' ? rootTypes : manifest.types;
2977
+ if (typeof typesPath !== 'string') {
2978
+ return false;
2979
+ }
2980
+ // Every declaration file the root re-exports from, followed through relative
2981
+ // `export … from` specifiers at any depth; the name is searched as text, so a
2982
+ // named re-export of it counts as well.
2983
+ const relativeFrom = /\bexport\b[^;]*?\bfrom\s*['"](\.{1,2}\/[^'"]+)['"]/gu;
2984
+ const packageStar = /\bexport\s*(?:type\s*)?\*\s*(?:as\s+[\w$]+\s*)?from\s*['"](?!\.{1,2}\/)[^'"]+['"]/u;
2985
+ const nameUsed = new RegExp(`(?<![\\w$])${exportName.replace(/\$/gu, '\\$')}(?![\\w$])`, 'u');
2986
+ const pending = [path.join(packageRoot, typesPath)];
2987
+ const visited = new Set();
2988
+ while (pending.length > 0) {
2989
+ const file = pending.pop() ?? '';
2990
+ if (visited.has(file)) {
2991
+ continue;
2992
+ }
2993
+ visited.add(file);
2994
+ if (visited.size > maxDeclarationFilesChecked) {
2995
+ return false;
2996
+ }
2997
+ let source;
2998
+ try {
2999
+ source = readExisting(file);
3000
+ }
3001
+ catch {
3002
+ source = undefined;
3003
+ }
3004
+ if (source === undefined || packageStar.test(source)) {
3005
+ // A module the check cannot read, or names from another package: do not refuse on a guess.
3006
+ return false;
3007
+ }
3008
+ if (nameUsed.test(source)) {
3009
+ return false;
3010
+ }
3011
+ for (const match of source.matchAll(relativeFrom)) {
3012
+ const candidates = declarationCandidates(path.join(path.dirname(file), match[1] ?? ''));
3013
+ pending.push(candidates.find((candidate) => existsSync(candidate)) ?? candidates[0] ?? file);
3014
+ }
3015
+ }
3016
+ return true;
3017
+ }
3018
+ /** The game's installed `packageName` manifest, or `undefined` when it is missing or unreadable. */
3019
+ function readInstalledManifest(gameRoot, packageName) {
3020
+ try {
3021
+ const source = readExisting(path.join(gameRoot, 'node_modules', packageName, 'package.json'));
3022
+ return source === undefined
3023
+ ? undefined
3024
+ : parseJsonObject(source, `${packageName}/package.json`);
3025
+ }
3026
+ catch {
3027
+ return undefined;
3028
+ }
3029
+ }
3030
+ /** The installed package's version, for a message. */
3031
+ function installedPackageVersion(gameRoot, packageName) {
3032
+ const version = readInstalledManifest(gameRoot, packageName)?.version;
3033
+ return typeof version === 'string' ? version : 'unknown';
3034
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitGitSha": "5e470aab672ae58128c6834786aadce5b41b484c",
2
+ "kitGitSha": "453ff66c7af9ff7623ef655b7ddbe311642159c6",
3
3
  "kitDirty": false,
4
- "packageVersion": "0.55.0"
4
+ "packageVersion": "0.57.0"
5
5
  }