@bleedingdev/modern-js-create 3.2.0-ultramodern.32 → 3.2.0-ultramodern.33

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.
Files changed (2) hide show
  1. package/dist/index.js +36 -534
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1445,21 +1445,10 @@ const zephyrRspackPlugin = () => ({
1445
1445
  const appId = '${app.id}';
1446
1446
  const cloudflareWorkerName = '${createCloudflareWorkerName(scope, app)}';
1447
1447
  const port = Number(process.env['${app.portEnv}'] ?? ${app.port});
1448
- const configuredSiteUrl = process.env['MODERN_PUBLIC_SITE_URL'];
1449
- const hasConfiguredSiteUrl =
1450
- typeof configuredSiteUrl === 'string' && configuredSiteUrl.length > 0;
1451
- const isProductionBuild =
1452
- process.env['NODE_ENV'] === 'production' || process.argv.includes('build');
1453
-
1454
- if (isProductionBuild && !hasConfiguredSiteUrl) {
1455
- throw new Error(
1456
- 'MODERN_PUBLIC_SITE_URL must be set for production builds so canonical and hreflang URLs use the deployed origin.',
1457
- );
1458
- }
1459
-
1460
- const siteUrl = hasConfiguredSiteUrl
1461
- ? configuredSiteUrl
1462
- : \`http://localhost:\${port}\`;
1448
+ const siteUrl =
1449
+ process.env['MODERN_PUBLIC_SITE_URL'] ??
1450
+ process.env['${createCloudflarePublicUrlEnv(app)}'] ??
1451
+ \`http://localhost:\${port}\`;
1463
1452
 
1464
1453
  export default defineConfig(
1465
1454
  presetUltramodern(
@@ -2620,7 +2609,6 @@ export default function ShellHome() {
2620
2609
  `;
2621
2610
  }
2622
2611
  function createRemotePage(app) {
2623
- if ('remote-commerce' === app.id) return createCommerceRemotePage(app);
2624
2612
  const effectBffImport = appHasEffectApi(app) ? `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
2625
2613
  import { Helmet } from '@modern-js/runtime/head';
2626
2614
  import { useLocation } from '@modern-js/plugin-tanstack/runtime';
@@ -2685,404 +2673,6 @@ ${effectBffMarkup} </main>
2685
2673
  }
2686
2674
  `;
2687
2675
  }
2688
- function createCommerceRemotePage(app) {
2689
- return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
2690
- import { Helmet } from '@modern-js/runtime/head';
2691
- import { useLocation } from '@modern-js/plugin-tanstack/runtime';
2692
- import { useEffect, useState, type CSSProperties } from 'react';
2693
- import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
2694
- import { ultramodernUiMarker } from '../../ultramodern-build';
2695
-
2696
- const languageCodes = ['en', 'cs'] as const;
2697
-
2698
- const boundaryDefinitions = [
2699
- {
2700
- color: '#ff5a57',
2701
- id: 'explore',
2702
- labelKey: 'commerce.boundaries.explore',
2703
- },
2704
- {
2705
- color: '#24d671',
2706
- id: 'decide',
2707
- labelKey: 'commerce.boundaries.decide',
2708
- },
2709
- {
2710
- color: '#f4d044',
2711
- id: 'checkout',
2712
- labelKey: 'commerce.boundaries.checkout',
2713
- },
2714
- ] as const;
2715
-
2716
- type BoundaryId = (typeof boundaryDefinitions)[number]['id'];
2717
- type BoundaryDefinition = (typeof boundaryDefinitions)[number];
2718
-
2719
- const boundaryMetadata: Record<BoundaryId, BoundaryDefinition> = {
2720
- checkout: boundaryDefinitions[2],
2721
- decide: boundaryDefinitions[1],
2722
- explore: boundaryDefinitions[0],
2723
- };
2724
-
2725
- const products = [
2726
- {
2727
- id: 'field-loader-112',
2728
- titleKey: 'commerce.products.fieldLoader.title',
2729
- descriptionKey: 'commerce.products.fieldLoader.description',
2730
- priceKey: 'commerce.products.fieldLoader.price',
2731
- powerKey: 'commerce.products.fieldLoader.power',
2732
- availabilityKey: 'commerce.products.fieldLoader.availability',
2733
- },
2734
- {
2735
- id: 'orchard-tractor',
2736
- titleKey: 'commerce.products.orchard.title',
2737
- badgeKey: 'commerce.products.orchard.badge',
2738
- },
2739
- {
2740
- id: 'autonomy-kit',
2741
- titleKey: 'commerce.products.autonomy.title',
2742
- badgeKey: 'commerce.products.autonomy.badge',
2743
- },
2744
- ] as const;
2745
-
2746
- type ProductId = (typeof products)[number]['id'];
2747
- type CartState = Partial<Record<ProductId, number>>;
2748
-
2749
- type BoundaryLabels = Record<BoundaryId, string>;
2750
- type BoundaryBox = {
2751
- color: string;
2752
- height: number;
2753
- id: BoundaryId;
2754
- label: string;
2755
- labelPlacement: 'above' | 'inside';
2756
- left: number;
2757
- top: number;
2758
- width: number;
2759
- };
2760
-
2761
- const featuredProduct = products[0];
2762
- const recommendations = [products[1], products[2]] as const;
2763
-
2764
- ${createLocalizedHeadComponent()}
2765
- const isBoundaryId = (value: string): value is BoundaryId =>
2766
- Object.prototype.hasOwnProperty.call(boundaryMetadata, value);
2767
-
2768
- function collectBoundaryBoxes(labels: BoundaryLabels): BoundaryBox[] {
2769
- return Array.from(
2770
- document.querySelectorAll<HTMLElement>('[data-boundary], [data-boundary-page]'),
2771
- )
2772
- .map(element => {
2773
- const id = element.dataset.boundary ?? element.dataset.boundaryPage;
2774
-
2775
- if (id === undefined || !isBoundaryId(id)) {
2776
- return undefined;
2777
- }
2778
-
2779
- const rect = element.getBoundingClientRect();
2780
-
2781
- if (rect.width <= 0 || rect.height <= 0) {
2782
- return undefined;
2783
- }
2784
-
2785
- return {
2786
- color: boundaryMetadata[id].color,
2787
- height: rect.height,
2788
- id,
2789
- label: labels[id],
2790
- labelPlacement: rect.top > 28 ? 'above' : 'inside',
2791
- left: rect.left,
2792
- top: rect.top,
2793
- width: rect.width,
2794
- };
2795
- })
2796
- .filter((box): box is BoundaryBox => box !== undefined);
2797
- }
2798
-
2799
- function BoundaryOverlay({
2800
- labels,
2801
- visible,
2802
- }: {
2803
- labels: BoundaryLabels;
2804
- visible: boolean;
2805
- }) {
2806
- const [boxes, setBoxes] = useState<BoundaryBox[]>([]);
2807
-
2808
- useEffect(() => {
2809
- if (!visible) {
2810
- setBoxes([]);
2811
- return;
2812
- }
2813
-
2814
- let animationFrame = 0;
2815
- const update = () => {
2816
- cancelAnimationFrame(animationFrame);
2817
- animationFrame = requestAnimationFrame(() => {
2818
- setBoxes(collectBoundaryBoxes(labels));
2819
- });
2820
- };
2821
- const observer = new ResizeObserver(update);
2822
-
2823
- observer.observe(document.body);
2824
- update();
2825
- window.addEventListener('resize', update);
2826
- window.addEventListener('scroll', update, true);
2827
-
2828
- return () => {
2829
- cancelAnimationFrame(animationFrame);
2830
- observer.disconnect();
2831
- window.removeEventListener('resize', update);
2832
- window.removeEventListener('scroll', update, true);
2833
- };
2834
- }, [labels, visible]);
2835
-
2836
- if (!visible) {
2837
- return null;
2838
- }
2839
-
2840
- return (
2841
- <div aria-hidden="true" className="boundary-overlay">
2842
- {boxes.map((box, index) => (
2843
- <div
2844
- className="boundary-overlay__box"
2845
- data-boundary-id={box.id}
2846
- data-label-placement={box.labelPlacement}
2847
- key={\`\${box.id}-\${index}\`}
2848
- style={{
2849
- '--boundary-color': box.color,
2850
- height: box.height,
2851
- left: box.left,
2852
- top: box.top,
2853
- width: box.width,
2854
- } as CSSProperties}
2855
- >
2856
- <span className="boundary-overlay__label">{box.label}</span>
2857
- </div>
2858
- ))}
2859
- </div>
2860
- );
2861
- }
2862
-
2863
- export default function ${toPascalCase(app.id)}Home() {
2864
- const { i18nInstance, language } = useModernI18n();
2865
- const t = i18nInstance.t.bind(i18nInstance);
2866
- const location = useLocation();
2867
- const suffix = locationSuffix(location);
2868
- const [cart, setCart] = useState<CartState>({});
2869
- const [showBoundaries, setShowBoundaries] = useState(false);
2870
- const [effectApiStatus, setEffectApiStatus] = useState('pending');
2871
- const boundaryLabels = {
2872
- checkout: t('commerce.boundaries.checkout'),
2873
- decide: t('commerce.boundaries.decide'),
2874
- explore: t('commerce.boundaries.explore'),
2875
- } satisfies BoundaryLabels;
2876
- const cartLines = products
2877
- .map(product => ({
2878
- product,
2879
- quantity: cart[product.id] ?? 0,
2880
- }))
2881
- .filter(line => line.quantity > 0);
2882
- const cartCount = cartLines.reduce((total, line) => total + line.quantity, 0);
2883
-
2884
- useEffect(() => {
2885
- void fetch('${effectApiPrefix(app)}/effect/${effectApiStem(app)}?limit=1', {
2886
- headers: {
2887
- accept: 'application/json',
2888
- },
2889
- })
2890
- .then(response => {
2891
- if (!response.ok) {
2892
- throw new Error(\`Effect BFF request failed: \${response.status}\`);
2893
- }
2894
-
2895
- return response.json() as Promise<{ items?: Array<{ title?: string }> }>;
2896
- })
2897
- .then(data => {
2898
- setEffectApiStatus(data.items[0]?.title ?? 'empty');
2899
- })
2900
- .catch(() => {
2901
- setEffectApiStatus('unavailable');
2902
- });
2903
- }, []);
2904
-
2905
- const addToCart = (id: ProductId) => {
2906
- setCart(current => ({
2907
- ...current,
2908
- [id]: (current[id] ?? 0) + 1,
2909
- }));
2910
- };
2911
-
2912
- const reduceQuantity = (id: ProductId) => {
2913
- setCart(current => {
2914
- const quantity = current[id] ?? 0;
2915
- const next = { ...current };
2916
-
2917
- if (quantity <= 1) {
2918
- delete next[id];
2919
- } else {
2920
- next[id] = quantity - 1;
2921
- }
2922
-
2923
- return next;
2924
- });
2925
- };
2926
-
2927
- const removeFromCart = (id: ProductId) => {
2928
- setCart(current => {
2929
- const next = { ...current };
2930
-
2931
- delete next[id];
2932
- return next;
2933
- });
2934
- };
2935
-
2936
- return (
2937
- <main className="commerce-shell">
2938
- <LocalizedHead />
2939
- <BoundaryOverlay labels={boundaryLabels} visible={showBoundaries} />
2940
- <header className="commerce-header" data-boundary="explore">
2941
- <strong className="commerce-logo">{t('commerce.brand')}</strong>
2942
- <nav aria-label={t('commerce.navigation.primary')} className="commerce-nav">
2943
- <a className="commerce-pill" href="#machines">
2944
- {t('commerce.navigation.machines')}
2945
- </a>
2946
- <a className="commerce-pill" href="#checkout">
2947
- {t('commerce.navigation.checkout')}
2948
- </a>
2949
- </nav>
2950
- <div className="commerce-actions">
2951
- <a className="commerce-cart-button" data-boundary="checkout" href="#cart">
2952
- {t('commerce.cart.button', { count: cartCount })}
2953
- </a>
2954
- <nav aria-label={t('commerce.language.switcher')} className="commerce-language">
2955
- {languageCodes.map(code => (
2956
- <a
2957
- aria-current={language === code ? 'page' : undefined}
2958
- className="commerce-pill"
2959
- href={\`\${localizedPath(location.pathname, code)}\${suffix}\`}
2960
- key={code}
2961
- >
2962
- {t(\`commerce.language.\${code}\`)}
2963
- </a>
2964
- ))}
2965
- </nav>
2966
- </div>
2967
- </header>
2968
-
2969
- <div className="commerce-page">
2970
- <section className="commerce-product" data-boundary-page="decide" id="machines">
2971
- <div
2972
- aria-label={t('commerce.products.fieldLoader.imageAlt')}
2973
- className="commerce-product-media"
2974
- role="img"
2975
- />
2976
- <div>
2977
- <p className="commerce-eyebrow">{t('commerce.detail.eyebrow')}</p>
2978
- <h1 className="commerce-title">{t(featuredProduct.titleKey)}</h1>
2979
- <p className="commerce-lede">{t(featuredProduct.descriptionKey)}</p>
2980
- <div className="commerce-facts">
2981
- <div className="commerce-fact">
2982
- <span>{t('commerce.detail.price')}</span>
2983
- <strong>{t(featuredProduct.priceKey)}</strong>
2984
- </div>
2985
- <div className="commerce-fact">
2986
- <span>{t('commerce.detail.power')}</span>
2987
- <strong>{t(featuredProduct.powerKey)}</strong>
2988
- </div>
2989
- <div className="commerce-fact">
2990
- <span>{t('commerce.detail.availability')}</span>
2991
- <strong>{t(featuredProduct.availabilityKey)}</strong>
2992
- </div>
2993
- </div>
2994
- <div className="commerce-checkout" data-boundary="checkout" id="checkout">
2995
- <button
2996
- className="commerce-button"
2997
- onClick={() => addToCart(featuredProduct.id)}
2998
- type="button"
2999
- >
3000
- {t('commerce.cart.add')}
3001
- </button>
3002
- <a className="commerce-link-button" href="#cart">
3003
- {t('commerce.cart.view')}
3004
- </a>
3005
- </div>
3006
- </div>
3007
- </section>
3008
-
3009
- <section data-boundary="explore">
3010
- <h2 className="commerce-section-title">{t('commerce.recommendations.title')}</h2>
3011
- <div className="commerce-grid">
3012
- {recommendations.map(product => (
3013
- <article className="commerce-card" key={product.id}>
3014
- <span>{t(product.badgeKey)}</span>
3015
- <strong>{t(product.titleKey)}</strong>
3016
- </article>
3017
- ))}
3018
- </div>
3019
- </section>
3020
-
3021
- <section className="commerce-cart-panel" data-boundary="checkout" id="cart">
3022
- <h2>{t('commerce.cart.title')}</h2>
3023
- {cartLines.length === 0 ? (
3024
- <p>{t('commerce.cart.empty')}</p>
3025
- ) : (
3026
- cartLines.map(line => (
3027
- <div className="commerce-cart-line" key={line.product.id}>
3028
- <strong>{t(line.product.titleKey)}</strong>
3029
- <div className="commerce-quantity">
3030
- <button
3031
- aria-label={t('commerce.cart.decrease', {
3032
- name: t(line.product.titleKey),
3033
- })}
3034
- className="commerce-quantity-button"
3035
- onClick={() => reduceQuantity(line.product.id)}
3036
- type="button"
3037
- >
3038
- -
3039
- </button>
3040
- <span>{line.quantity}</span>
3041
- <button
3042
- aria-label={t('commerce.cart.increase', {
3043
- name: t(line.product.titleKey),
3044
- })}
3045
- className="commerce-quantity-button"
3046
- onClick={() => addToCart(line.product.id)}
3047
- type="button"
3048
- >
3049
- +
3050
- </button>
3051
- <button
3052
- className="commerce-link-button"
3053
- onClick={() => removeFromCart(line.product.id)}
3054
- type="button"
3055
- >
3056
- {t('commerce.cart.remove')}
3057
- </button>
3058
- </div>
3059
- </div>
3060
- ))
3061
- )}
3062
- </section>
3063
- </div>
3064
-
3065
- <footer className="commerce-footer" data-boundary="explore">
3066
- <span>{t('commerce.footer.stack')}</span>
3067
- <span data-testid="effect-bff-status">{effectApiStatus}</span>
3068
- <span data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
3069
- {ultramodernUiMarker.appId}:{ultramodernUiMarker.version}
3070
- </span>
3071
- </footer>
3072
-
3073
- <label className="commerce-boundary-toggle">
3074
- <input
3075
- checked={showBoundaries}
3076
- onChange={event => setShowBoundaries(event.currentTarget.checked)}
3077
- type="checkbox"
3078
- />
3079
- {t('commerce.boundaries.toggle')}
3080
- </label>
3081
- </main>
3082
- );
3083
- }
3084
- `;
3085
- }
3086
2676
  function createLayout(appId) {
3087
2677
  return `import { Outlet } from '@modern-js/plugin-tanstack/runtime';
3088
2678
  import './index.css';
@@ -3203,69 +2793,6 @@ function createAppLocaleMessages(app, language) {
3203
2793
  role: domain,
3204
2794
  title: `${app.displayName} CZ`
3205
2795
  };
3206
- if ('commerce' === domain) return {
3207
- commerce: {
3208
- boundaries: {
3209
- checkout: 'en' === language ? 'checkout' : 'pokladna',
3210
- decide: 'en' === language ? 'decide' : 'rozhodování',
3211
- explore: 'en' === language ? 'explore' : 'procházení',
3212
- toggle: 'en' === language ? 'show team boundaries' : 'zobrazit hranice týmů'
3213
- },
3214
- brand: 'Acre & Iron',
3215
- cart: {
3216
- add: 'en' === language ? 'Add to cart' : 'Přidat do košíku',
3217
- button: 'en' === language ? 'Your cart ({{count}})' : 'Košík ({{count}})',
3218
- decrease: 'en' === language ? 'Decrease {{name}} quantity' : 'Snížit množství položky {{name}}',
3219
- empty: 'en' === language ? 'Your cart is empty.' : 'Košík je prázdný.',
3220
- increase: 'en' === language ? 'Increase {{name}} quantity' : 'Zvýšit množství položky {{name}}',
3221
- remove: 'en' === language ? 'Remove' : 'Odebrat',
3222
- title: 'en' === language ? 'Cart' : 'Košík',
3223
- view: 'en' === language ? 'View cart' : 'Zobrazit košík'
3224
- },
3225
- detail: {
3226
- availability: 'en' === language ? 'Availability' : 'Dostupnost',
3227
- eyebrow: 'en' === language ? 'Machine detail' : 'Detail stroje',
3228
- power: 'en' === language ? 'Power' : 'Výkon',
3229
- price: 'en' === language ? 'Price' : 'Cena'
3230
- },
3231
- footer: {
3232
- stack: 'en' === language ? 'SPA, SSR-ready Module Federation, React, Effect BFF' : 'SPA, SSR-ready Module Federation, React, Effect BFF'
3233
- },
3234
- language: {
3235
- cs: 'en' === language ? 'Czech' : 'Čeština',
3236
- en: 'en' === language ? 'English' : 'Angličtina',
3237
- switcher: 'en' === language ? 'Language' : 'Jazyk'
3238
- },
3239
- navigation: {
3240
- checkout: 'en' === language ? 'Checkout' : 'Pokladna',
3241
- machines: 'en' === language ? 'Machines' : 'Stroje',
3242
- primary: 'en' === language ? 'Primary commerce navigation' : 'Hlavní navigace obchodu'
3243
- },
3244
- products: {
3245
- autonomy: {
3246
- badge: 'en' === language ? 'AI-first option' : 'AI varianta',
3247
- title: 'en' === language ? 'Autonomy Retrofit Kit' : 'Sada pro autonomní řízení'
3248
- },
3249
- fieldLoader: {
3250
- availability: 'en' === language ? 'In stock' : 'Skladem',
3251
- description: 'en' === language ? 'A loader-ready tractor for feed, hay, gravel, and winter road work.' : 'Traktor připravený na nakladač pro krmivo, seno, štěrk i zimní údržbu cest.',
3252
- imageAlt: 'en' === language ? 'Field Loader 112 tractor working on a bright farm lane' : 'Traktor Field Loader 112 pracuje na světlé polní cestě',
3253
- power: '112 hp',
3254
- price: 'EUR 42,500',
3255
- title: 'Field Loader 112'
3256
- },
3257
- orchard: {
3258
- badge: 'en' === language ? 'Best for tight rows' : 'Nejlepší do úzkých řádků',
3259
- title: 'en' === language ? 'Narrow Orchard Tractor' : 'Úzký sadový traktor'
3260
- }
3261
- },
3262
- recommendations: {
3263
- title: 'en' === language ? 'Compare alternatives' : 'Porovnat alternativy'
3264
- },
3265
- role: 'en' === language ? 'commerce' : 'obchod',
3266
- title: 'en' === language ? app.displayName : czechLabel.title
3267
- }
3268
- };
3269
2796
  return {
3270
2797
  [domain]: {
3271
2798
  language: {
@@ -4343,48 +3870,6 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4343
3870
  apiSurface: 'effect-bff'
4344
3871
  } : {}
4345
3872
  },
4346
- ...'commerce' === app.domain ? {
4347
- boundaryVisualization: {
4348
- mode: 'overlay',
4349
- layoutAffecting: false,
4350
- toggle: 'user-controlled',
4351
- boundaries: [
4352
- {
4353
- id: 'explore',
4354
- labelKey: 'commerce.boundaries.explore',
4355
- owner: 'team-explore',
4356
- color: '#ff5a57',
4357
- owns: [
4358
- 'header',
4359
- 'footer',
4360
- 'recommendations',
4361
- 'catalog'
4362
- ]
4363
- },
4364
- {
4365
- id: 'decide',
4366
- labelKey: 'commerce.boundaries.decide',
4367
- owner: 'team-decide',
4368
- color: '#24d671',
4369
- owns: [
4370
- 'product-detail',
4371
- 'variant-selection'
4372
- ]
4373
- },
4374
- {
4375
- id: 'checkout',
4376
- labelKey: 'commerce.boundaries.checkout',
4377
- owner: 'team-checkout',
4378
- color: '#f4d044',
4379
- owns: [
4380
- 'add-to-cart',
4381
- 'cart-link',
4382
- 'cart-lines'
4383
- ]
4384
- }
4385
- ]
4386
- }
4387
- } : {},
4388
3873
  ...appHasEffectApi(app) ? {
4389
3874
  effect: {
4390
3875
  runtime: 'effect',
@@ -4553,6 +4038,13 @@ function createAssertMfTypesScript(remotes = remoteApps) {
4553
4038
  import path from 'node:path';
4554
4039
 
4555
4040
  const root = process.cwd();
4041
+ const generatedContractPath = path.join(
4042
+ root,
4043
+ '.modernjs/ultramodern-generated-contract.json',
4044
+ );
4045
+ const generatedContract = fs.existsSync(generatedContractPath)
4046
+ ? JSON.parse(fs.readFileSync(generatedContractPath, 'utf-8'))
4047
+ : undefined;
4556
4048
  const defaultAppDirs = ${JSON.stringify(remotes.map((remote)=>remote.directory), null, 2)};
4557
4049
 
4558
4050
  const candidateDirs = process.argv.slice(2);
@@ -4570,20 +4062,20 @@ for (const appDir of appDirs) {
4570
4062
  );
4571
4063
  }
4572
4064
 
4573
- const config = fs.readFileSync(configPath, 'utf-8');
4574
- if (config.includes('dts: false')) {
4575
- throw new Error(
4576
- \`Module Federation DTS must stay enabled: \${path.relative(root, configPath)}\`,
4577
- );
4578
- }
4579
-
4580
- if (!config.includes("compilerInstance: '--package typescript -- tsc'")) {
4065
+ const contractEntry = generatedContract?.apps?.find(
4066
+ app => app.path === appDir.replace(/\\\\/g, '/'),
4067
+ );
4068
+ if (
4069
+ contractEntry &&
4070
+ contractEntry.moduleFederation?.dts?.compilerInstance !==
4071
+ '--package typescript -- tsc'
4072
+ ) {
4581
4073
  throw new Error(
4582
- \`Module Federation DTS must use the workspace TypeScript compiler: \${path.relative(root, configPath)}\`,
4074
+ \`Module Federation DTS must use the workspace TypeScript compiler: \${appDir}\`,
4583
4075
  );
4584
4076
  }
4585
4077
 
4586
- if (!config.includes('exposes:')) {
4078
+ if (contractEntry && contractEntry.moduleFederation?.exposes?.length === 0) {
4587
4079
  continue;
4588
4080
  }
4589
4081
 
@@ -4909,13 +4401,23 @@ function createCloudflareVersionProofScript() {
4909
4401
  return `#!/usr/bin/env node
4910
4402
  import fs from 'node:fs';
4911
4403
  import path from 'node:path';
4404
+ import { fileURLToPath } from 'node:url';
4912
4405
 
4913
- const contractPath = '.modernjs/ultramodern-generated-contract.json';
4914
- const defaultOut =
4915
- '.codex/reports/cloudflare-version-proof/public-url-proof.json';
4406
+ const workspaceRoot = path.resolve(
4407
+ path.dirname(fileURLToPath(import.meta.url)),
4408
+ '..',
4409
+ );
4410
+ const contractPath = path.join(
4411
+ workspaceRoot,
4412
+ '.modernjs/ultramodern-generated-contract.json',
4413
+ );
4414
+ const defaultOut = path.join(
4415
+ workspaceRoot,
4416
+ '.codex/reports/cloudflare-version-proof/public-url-proof.json',
4417
+ );
4916
4418
 
4917
- function readJson(relativePath) {
4918
- return JSON.parse(fs.readFileSync(path.resolve(relativePath), 'utf8'));
4419
+ function readJson(filePath) {
4420
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
4919
4421
  }
4920
4422
 
4921
4423
  function parseArgs(argv) {
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.32",
24
+ "version": "3.2.0-ultramodern.33",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {