@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.
- package/dist/index.js +48 -535
- 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
|
|
1450
|
-
|
|
1451
|
-
const
|
|
1452
|
-
|
|
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
|
|
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
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
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: \${
|
|
4085
|
+
\`Module Federation DTS must use the workspace TypeScript compiler: \${appDir}\`,
|
|
4583
4086
|
);
|
|
4584
4087
|
}
|
|
4585
4088
|
|
|
4586
|
-
if (
|
|
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
|
|
4914
|
-
|
|
4915
|
-
'
|
|
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(
|
|
4918
|
-
return JSON.parse(fs.readFileSync(
|
|
4430
|
+
function readJson(filePath) {
|
|
4431
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
4919
4432
|
}
|
|
4920
4433
|
|
|
4921
4434
|
function parseArgs(argv) {
|