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

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 +48 -535
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1445,21 +1445,11 @@ 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 configuredSiteUrl = process.env['MODERN_PUBLIC_SITE_URL']?.trim();
1449
+ const configuredCloudflareUrl =
1450
+ process.env['${createCloudflarePublicUrlEnv(app)}']?.trim();
1451
+ const siteUrl =
1452
+ configuredSiteUrl || configuredCloudflareUrl || \`http://localhost:\${port}\`;
1463
1453
 
1464
1454
  export default defineConfig(
1465
1455
  presetUltramodern(
@@ -2620,7 +2610,6 @@ export default function ShellHome() {
2620
2610
  `;
2621
2611
  }
2622
2612
  function createRemotePage(app) {
2623
- if ('remote-commerce' === app.id) return createCommerceRemotePage(app);
2624
2613
  const effectBffImport = appHasEffectApi(app) ? `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
2625
2614
  import { Helmet } from '@modern-js/runtime/head';
2626
2615
  import { useLocation } from '@modern-js/plugin-tanstack/runtime';
@@ -2685,404 +2674,6 @@ ${effectBffMarkup} </main>
2685
2674
  }
2686
2675
  `;
2687
2676
  }
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
2677
  function createLayout(appId) {
3087
2678
  return `import { Outlet } from '@modern-js/plugin-tanstack/runtime';
3088
2679
  import './index.css';
@@ -3203,69 +2794,6 @@ function createAppLocaleMessages(app, language) {
3203
2794
  role: domain,
3204
2795
  title: `${app.displayName} CZ`
3205
2796
  };
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
2797
  return {
3270
2798
  [domain]: {
3271
2799
  language: {
@@ -4343,48 +3871,6 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4343
3871
  apiSurface: 'effect-bff'
4344
3872
  } : {}
4345
3873
  },
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
3874
  ...appHasEffectApi(app) ? {
4389
3875
  effect: {
4390
3876
  runtime: 'effect',
@@ -4553,9 +4039,26 @@ function createAssertMfTypesScript(remotes = remoteApps) {
4553
4039
  import path from 'node:path';
4554
4040
 
4555
4041
  const root = process.cwd();
4042
+ const generatedContractPath = path.join(
4043
+ root,
4044
+ '.modernjs/ultramodern-generated-contract.json',
4045
+ );
4046
+ const generatedContract = fs.existsSync(generatedContractPath)
4047
+ ? JSON.parse(fs.readFileSync(generatedContractPath, 'utf-8'))
4048
+ : undefined;
4556
4049
  const defaultAppDirs = ${JSON.stringify(remotes.map((remote)=>remote.directory), null, 2)};
4557
4050
 
4558
- const candidateDirs = process.argv.slice(2);
4051
+ const args = process.argv.slice(2);
4052
+ if (args.includes('--help') || args.includes('-h')) {
4053
+ process.stdout.write(\`Usage:
4054
+ node scripts/assert-mf-types.mjs [app-dir...]
4055
+
4056
+ Checks that every Module Federation remote with exposed modules emitted a non-empty dist/@mf-types.zip archive and uses the workspace TypeScript compiler.
4057
+ \`);
4058
+ process.exit(0);
4059
+ }
4060
+
4061
+ const candidateDirs = args;
4559
4062
  const appDirs = candidateDirs.length
4560
4063
  ? candidateDirs
4561
4064
  : fs.existsSync(path.join(root, 'module-federation.config.ts'))
@@ -4570,20 +4073,20 @@ for (const appDir of appDirs) {
4570
4073
  );
4571
4074
  }
4572
4075
 
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'")) {
4076
+ const contractEntry = generatedContract?.apps?.find(
4077
+ app => app.path === appDir.replace(/\\\\/g, '/'),
4078
+ );
4079
+ if (
4080
+ contractEntry &&
4081
+ contractEntry.moduleFederation?.dts?.compilerInstance !==
4082
+ '--package typescript -- tsc'
4083
+ ) {
4581
4084
  throw new Error(
4582
- \`Module Federation DTS must use the workspace TypeScript compiler: \${path.relative(root, configPath)}\`,
4085
+ \`Module Federation DTS must use the workspace TypeScript compiler: \${appDir}\`,
4583
4086
  );
4584
4087
  }
4585
4088
 
4586
- if (!config.includes('exposes:')) {
4089
+ if (contractEntry && contractEntry.moduleFederation?.exposes?.length === 0) {
4587
4090
  continue;
4588
4091
  }
4589
4092
 
@@ -4909,13 +4412,23 @@ function createCloudflareVersionProofScript() {
4909
4412
  return `#!/usr/bin/env node
4910
4413
  import fs from 'node:fs';
4911
4414
  import path from 'node:path';
4415
+ import { fileURLToPath } from 'node:url';
4912
4416
 
4913
- const contractPath = '.modernjs/ultramodern-generated-contract.json';
4914
- const defaultOut =
4915
- '.codex/reports/cloudflare-version-proof/public-url-proof.json';
4417
+ const workspaceRoot = path.resolve(
4418
+ path.dirname(fileURLToPath(import.meta.url)),
4419
+ '..',
4420
+ );
4421
+ const contractPath = path.join(
4422
+ workspaceRoot,
4423
+ '.modernjs/ultramodern-generated-contract.json',
4424
+ );
4425
+ const defaultOut = path.join(
4426
+ workspaceRoot,
4427
+ '.codex/reports/cloudflare-version-proof/public-url-proof.json',
4428
+ );
4916
4429
 
4917
- function readJson(relativePath) {
4918
- return JSON.parse(fs.readFileSync(path.resolve(relativePath), 'utf8'));
4430
+ function readJson(filePath) {
4431
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
4919
4432
  }
4920
4433
 
4921
4434
  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.34",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {