@bleedingdev/modern-js-create 3.2.0-ultramodern.52 → 3.2.0-ultramodern.54
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 +521 -919
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1095,6 +1095,7 @@ function appDependencies(scope, packageSource, app) {
|
|
|
1095
1095
|
dependencies['@modern-js/plugin-bff'] = modernPackageSpecifier('@modern-js/plugin-bff', packageSource);
|
|
1096
1096
|
for (const remote of verticalEffectApps())dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
1097
1097
|
}
|
|
1098
|
+
for (const remote of resolveRemoteRefs(app))dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
1098
1099
|
if (appHasEffectApi(app)) dependencies['@modern-js/plugin-bff'] = modernPackageSpecifier('@modern-js/plugin-bff', packageSource);
|
|
1099
1100
|
return dependencies;
|
|
1100
1101
|
}
|
|
@@ -1305,6 +1306,10 @@ function createPackageTsConfig(packageDir, includeApi = false) {
|
|
|
1305
1306
|
};
|
|
1306
1307
|
}
|
|
1307
1308
|
function createAppPackage(scope, app, packageSource, enableTailwind) {
|
|
1309
|
+
const packageExports = Object.fromEntries(Object.entries(app.exposes ?? {}).map(([expose, source])=>[
|
|
1310
|
+
expose,
|
|
1311
|
+
source
|
|
1312
|
+
]));
|
|
1308
1313
|
const packageJson = {
|
|
1309
1314
|
private: true,
|
|
1310
1315
|
name: ultramodern_workspace_packageName(scope, app.packageSuffix),
|
|
@@ -1332,13 +1337,14 @@ function createAppPackage(scope, app, packageSource, enableTailwind) {
|
|
|
1332
1337
|
dependencies: appDependencies(scope, packageSource, app),
|
|
1333
1338
|
devDependencies: appDevDependencies(packageSource, enableTailwind)
|
|
1334
1339
|
};
|
|
1335
|
-
if (appHasEffectApi(app))
|
|
1340
|
+
if (appHasEffectApi(app)) Object.assign(packageExports, {
|
|
1336
1341
|
'./effect/client': `./src/effect/${app.effectApi.stem}-client.ts`,
|
|
1337
1342
|
'./shared/effect/api': './shared/effect/api.ts'
|
|
1338
|
-
};
|
|
1339
|
-
else if ('shell' === app.kind)
|
|
1343
|
+
});
|
|
1344
|
+
else if ('shell' === app.kind) Object.assign(packageExports, {
|
|
1340
1345
|
'./effect/clients': './src/effect/recommendations-client.ts'
|
|
1341
|
-
};
|
|
1346
|
+
});
|
|
1347
|
+
if (Object.keys(packageExports).length > 0) packageJson.exports = packageExports;
|
|
1342
1348
|
return packageJson;
|
|
1343
1349
|
}
|
|
1344
1350
|
function createServicePackage(scope, packageSource, enableTailwind, service = effectService) {
|
|
@@ -1436,6 +1442,8 @@ import { ultramodernLocalisedUrls } from './src/routes/ultramodern-route-metadat
|
|
|
1436
1442
|
type ZephyrRspackConfig = Parameters<ReturnType<typeof withZephyrRspack>>[0];
|
|
1437
1443
|
|
|
1438
1444
|
const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
|
|
1445
|
+
const cloudflareDeployEnabled =
|
|
1446
|
+
process.env['MODERNJS_DEPLOY'] === 'cloudflare';
|
|
1439
1447
|
|
|
1440
1448
|
const zephyrRspackPlugin = () => ({
|
|
1441
1449
|
name: 'ultramodern-zephyr-rspack-plugin',
|
|
@@ -1526,13 +1534,17 @@ ${bffPluginEntry} moduleFederationPlugin(),
|
|
|
1526
1534
|
]);
|
|
1527
1535
|
},
|
|
1528
1536
|
},
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1537
|
+
...(cloudflareDeployEnabled
|
|
1538
|
+
? {
|
|
1539
|
+
deploy: {
|
|
1540
|
+
target: 'cloudflare',
|
|
1541
|
+
worker: {
|
|
1542
|
+
name: cloudflareWorkerName,
|
|
1543
|
+
ssr: true,
|
|
1544
|
+
},
|
|
1545
|
+
},
|
|
1546
|
+
}
|
|
1547
|
+
: {}),
|
|
1536
1548
|
server: {
|
|
1537
1549
|
port,
|
|
1538
1550
|
publicDir: './locales',
|
|
@@ -1983,6 +1995,10 @@ function createAppEnvDts(app, remotes = remoteApps) {
|
|
|
1983
1995
|
return `/// <reference types='@modern-js/app-tools/types' />
|
|
1984
1996
|
|
|
1985
1997
|
declare const ULTRAMODERN_SITE_URL: string;
|
|
1998
|
+
declare module '*.svg' {
|
|
1999
|
+
const url: string;
|
|
2000
|
+
export default url;
|
|
2001
|
+
}
|
|
1986
2002
|
${remoteModuleDeclarations ? `\n${remoteModuleDeclarations}` : ''}`;
|
|
1987
2003
|
}
|
|
1988
2004
|
function createServiceModernConfigFor(service = effectService) {
|
|
@@ -2066,522 +2082,13 @@ function createCssTokenImport(scope) {
|
|
|
2066
2082
|
return `@import '${ultramodern_workspace_packageName(scope, 'shared-design-tokens')}/tokens.css';\n`;
|
|
2067
2083
|
}
|
|
2068
2084
|
function createShellStyles(enableTailwind, scope) {
|
|
2069
|
-
return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}
|
|
2070
|
-
|
|
2071
|
-
@layer ultramodern-shell-base {
|
|
2072
|
-
:root {
|
|
2073
|
-
color: var(--um-color-foreground);
|
|
2074
|
-
background: var(--um-color-canvas);
|
|
2075
|
-
font-family:
|
|
2076
|
-
Geist,
|
|
2077
|
-
Inter,
|
|
2078
|
-
ui-sans-serif,
|
|
2079
|
-
system-ui,
|
|
2080
|
-
-apple-system,
|
|
2081
|
-
BlinkMacSystemFont,
|
|
2082
|
-
"Segoe UI",
|
|
2083
|
-
sans-serif;
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
body {
|
|
2087
|
-
margin: 0;
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
main {
|
|
2091
|
-
min-height: 100vh;
|
|
2092
|
-
padding: 2rem;
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
nav {
|
|
2096
|
-
display: flex;
|
|
2097
|
-
gap: 0.75rem;
|
|
2098
|
-
margin-bottom: 2rem;
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
a {
|
|
2102
|
-
color: var(--um-color-link);
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
.commerce-shell {
|
|
2106
|
-
background: #f1eadc;
|
|
2107
|
-
color: #0b0a08;
|
|
2108
|
-
min-height: 100vh;
|
|
2109
|
-
padding: 1.5rem clamp(1rem, 4vw, 3rem) 4rem;
|
|
2110
|
-
}
|
|
2111
|
-
|
|
2112
|
-
.commerce-shell-actions {
|
|
2113
|
-
align-items: center;
|
|
2114
|
-
display: flex;
|
|
2115
|
-
flex-wrap: wrap;
|
|
2116
|
-
gap: 0.75rem;
|
|
2117
|
-
justify-content: flex-end;
|
|
2118
|
-
margin: -4.25rem auto 3rem;
|
|
2119
|
-
max-width: 88rem;
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
.commerce-language {
|
|
2123
|
-
margin: 0;
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
.commerce-page {
|
|
2127
|
-
margin: 3rem auto 0;
|
|
2128
|
-
max-width: 88rem;
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
.commerce-hero {
|
|
2132
|
-
padding: 4rem 0 2rem;
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
.commerce-eyebrow {
|
|
2136
|
-
color: #00624b;
|
|
2137
|
-
font-size: 0.85rem;
|
|
2138
|
-
font-weight: 850;
|
|
2139
|
-
letter-spacing: 0.16rem;
|
|
2140
|
-
text-transform: uppercase;
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
.commerce-title {
|
|
2144
|
-
font-size: clamp(2.5rem, 6vw, 4.8rem);
|
|
2145
|
-
line-height: 0.95;
|
|
2146
|
-
margin: 0.65rem 0 1.4rem;
|
|
2147
|
-
max-width: 58rem;
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
.commerce-lede {
|
|
2151
|
-
color: #555149;
|
|
2152
|
-
font-size: 1.2rem;
|
|
2153
|
-
line-height: 1.65;
|
|
2154
|
-
max-width: 42rem;
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
|
-
.commerce-checkout {
|
|
2158
|
-
align-items: center;
|
|
2159
|
-
display: flex;
|
|
2160
|
-
flex-wrap: wrap;
|
|
2161
|
-
gap: 0.75rem;
|
|
2162
|
-
margin-top: 1.5rem;
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
.commerce-pill,
|
|
2166
|
-
.commerce-button,
|
|
2167
|
-
.commerce-link-button,
|
|
2168
|
-
.commerce-cart-button {
|
|
2169
|
-
align-items: center;
|
|
2170
|
-
border-radius: 999px;
|
|
2171
|
-
border: 0.0625rem solid rgba(23, 23, 23, 0.14);
|
|
2172
|
-
box-shadow: 0 0.25rem 0.75rem rgba(20, 17, 10, 0.08);
|
|
2173
|
-
color: #14120d;
|
|
2174
|
-
display: inline-flex;
|
|
2175
|
-
font: inherit;
|
|
2176
|
-
font-weight: 750;
|
|
2177
|
-
justify-content: center;
|
|
2178
|
-
min-height: 2.5rem;
|
|
2179
|
-
padding: 0.65rem 1.05rem;
|
|
2180
|
-
text-decoration: none;
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
.commerce-button {
|
|
2184
|
-
background: #00624b;
|
|
2185
|
-
border-color: #00624b;
|
|
2186
|
-
color: #ffffff;
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
.commerce-link-button,
|
|
2190
|
-
.commerce-pill,
|
|
2191
|
-
.commerce-cart-button {
|
|
2192
|
-
background: rgba(255, 255, 255, 0.92);
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
.commerce-boundary-toggle {
|
|
2196
|
-
align-items: center;
|
|
2197
|
-
background: rgba(255, 255, 255, 0.94);
|
|
2198
|
-
border: 0.0625rem solid rgba(23, 23, 23, 0.12);
|
|
2199
|
-
border-radius: 0.8rem;
|
|
2200
|
-
bottom: 1.5rem;
|
|
2201
|
-
box-shadow: 0 0.75rem 2rem rgba(18, 15, 10, 0.14);
|
|
2202
|
-
color: #14120d;
|
|
2203
|
-
display: flex;
|
|
2204
|
-
gap: 0.65rem;
|
|
2205
|
-
left: 1.5rem;
|
|
2206
|
-
padding: 0.8rem 1rem;
|
|
2207
|
-
position: fixed;
|
|
2208
|
-
z-index: 80;
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
.commerce-boundary-toggle input {
|
|
2212
|
-
accent-color: #00624b;
|
|
2213
|
-
height: 1rem;
|
|
2214
|
-
width: 1rem;
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
@media (max-width: 860px) {
|
|
2218
|
-
.commerce-shell-actions {
|
|
2219
|
-
justify-content: flex-start;
|
|
2220
|
-
margin-top: 1rem;
|
|
2221
|
-
}
|
|
2085
|
+
return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}`;
|
|
2222
2086
|
}
|
|
2087
|
+
function createRemoteStyles(enableTailwind, scope, _app) {
|
|
2088
|
+
return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}`;
|
|
2223
2089
|
}
|
|
2224
|
-
|
|
2225
|
-
@
|
|
2226
|
-
.boundary-overlay {
|
|
2227
|
-
inset: 0;
|
|
2228
|
-
pointer-events: none;
|
|
2229
|
-
position: fixed;
|
|
2230
|
-
z-index: 70;
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
.boundary-overlay__box {
|
|
2234
|
-
border: 0.0625rem solid var(--boundary-color);
|
|
2235
|
-
border-radius: 0.55rem;
|
|
2236
|
-
box-shadow:
|
|
2237
|
-
0 0 0 0.0625rem rgba(255, 255, 255, 0.72),
|
|
2238
|
-
0 0.35rem 1.25rem color-mix(in srgb, var(--boundary-color) 20%, transparent);
|
|
2239
|
-
position: fixed;
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
.boundary-overlay__label {
|
|
2243
|
-
background: color-mix(in srgb, var(--boundary-color) 88%, white);
|
|
2244
|
-
border-radius: 999px;
|
|
2245
|
-
color: #0b0a08;
|
|
2246
|
-
font-size: 0.7rem;
|
|
2247
|
-
font-weight: 850;
|
|
2248
|
-
line-height: 1;
|
|
2249
|
-
padding: 0.3rem 0.55rem;
|
|
2250
|
-
position: absolute;
|
|
2251
|
-
right: 0.35rem;
|
|
2252
|
-
top: 0.35rem;
|
|
2253
|
-
white-space: nowrap;
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
|
-
.boundary-overlay__box[data-label-placement="above"] .boundary-overlay__label {
|
|
2257
|
-
bottom: calc(100% + 0.25rem);
|
|
2258
|
-
top: auto;
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
`;
|
|
2262
|
-
}
|
|
2263
|
-
function createRemoteStyles(enableTailwind, scope, app) {
|
|
2264
|
-
if ([
|
|
2265
|
-
'explore',
|
|
2266
|
-
'decide',
|
|
2267
|
-
'checkout'
|
|
2268
|
-
].includes(app.domain ?? '')) return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}
|
|
2269
|
-
|
|
2270
|
-
@layer ultramodern-remote-${app.domain} {
|
|
2271
|
-
.commerce-shell {
|
|
2272
|
-
background: #f1eadc;
|
|
2273
|
-
color: #0b0a08;
|
|
2274
|
-
min-height: 100vh;
|
|
2275
|
-
padding: 1.5rem clamp(1rem, 4vw, 3rem) 4rem;
|
|
2276
|
-
}
|
|
2277
|
-
|
|
2278
|
-
.commerce-header,
|
|
2279
|
-
.commerce-footer {
|
|
2280
|
-
align-items: center;
|
|
2281
|
-
background: rgba(255, 255, 255, 0.86);
|
|
2282
|
-
box-shadow: 0 0.625rem 1.875rem rgba(25, 20, 12, 0.08);
|
|
2283
|
-
display: flex;
|
|
2284
|
-
gap: 1.25rem;
|
|
2285
|
-
justify-content: space-between;
|
|
2286
|
-
margin: 0 auto;
|
|
2287
|
-
max-width: 88rem;
|
|
2288
|
-
padding: 1.25rem 1.75rem;
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
.commerce-logo {
|
|
2292
|
-
font-size: 1.35rem;
|
|
2293
|
-
font-weight: 800;
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
|
-
.commerce-nav,
|
|
2297
|
-
.commerce-actions,
|
|
2298
|
-
.commerce-language {
|
|
2299
|
-
align-items: center;
|
|
2300
|
-
display: flex;
|
|
2301
|
-
flex-wrap: wrap;
|
|
2302
|
-
gap: 0.75rem;
|
|
2303
|
-
}
|
|
2304
|
-
|
|
2305
|
-
.commerce-nav {
|
|
2306
|
-
margin: 0;
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
.commerce-pill,
|
|
2310
|
-
.commerce-button,
|
|
2311
|
-
.commerce-link-button,
|
|
2312
|
-
.commerce-cart-button,
|
|
2313
|
-
.commerce-quantity-button {
|
|
2314
|
-
align-items: center;
|
|
2315
|
-
border-radius: 999px;
|
|
2316
|
-
border: 0.0625rem solid rgba(23, 23, 23, 0.14);
|
|
2317
|
-
box-shadow: 0 0.25rem 0.75rem rgba(20, 17, 10, 0.08);
|
|
2318
|
-
color: #14120d;
|
|
2319
|
-
display: inline-flex;
|
|
2320
|
-
font: inherit;
|
|
2321
|
-
font-weight: 750;
|
|
2322
|
-
justify-content: center;
|
|
2323
|
-
min-height: 2.5rem;
|
|
2324
|
-
padding: 0.65rem 1.05rem;
|
|
2325
|
-
text-decoration: none;
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
.commerce-button {
|
|
2329
|
-
background: #00624b;
|
|
2330
|
-
border-color: #00624b;
|
|
2331
|
-
color: #ffffff;
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2334
|
-
.commerce-link-button,
|
|
2335
|
-
.commerce-pill,
|
|
2336
|
-
.commerce-cart-button,
|
|
2337
|
-
.commerce-quantity-button {
|
|
2338
|
-
background: rgba(255, 255, 255, 0.92);
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
.commerce-page {
|
|
2342
|
-
margin: 3rem auto 0;
|
|
2343
|
-
max-width: 88rem;
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
.commerce-product {
|
|
2347
|
-
align-items: center;
|
|
2348
|
-
display: grid;
|
|
2349
|
-
gap: clamp(2rem, 5vw, 4rem);
|
|
2350
|
-
grid-template-columns: minmax(0, 1fr) minmax(20rem, 0.95fr);
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
.commerce-product-media {
|
|
2354
|
-
aspect-ratio: 1 / 0.92;
|
|
2355
|
-
background:
|
|
2356
|
-
linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0) 42%),
|
|
2357
|
-
linear-gradient(135deg, #97c66d 0 20%, #6f9748 20% 34%, #d6c15d 34% 36%, #6a8f3e 36% 46%, #315824 46% 64%, #8bb85e 64% 100%);
|
|
2358
|
-
border: 1.25rem solid #ffe987;
|
|
2359
|
-
border-radius: 1.6rem;
|
|
2360
|
-
box-shadow: inset 0 -7rem 8rem rgba(58, 77, 35, 0.22);
|
|
2361
|
-
overflow: hidden;
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
.commerce-product-media::after {
|
|
2365
|
-
background:
|
|
2366
|
-
radial-gradient(circle at 27% 76%, #1e2422 0 5%, transparent 5.4%),
|
|
2367
|
-
radial-gradient(circle at 55% 76%, #1e2422 0 6%, transparent 6.4%),
|
|
2368
|
-
linear-gradient(0deg, #004b7b 0 100%);
|
|
2369
|
-
border-radius: 1.2rem;
|
|
2370
|
-
content: "";
|
|
2371
|
-
display: block;
|
|
2372
|
-
height: 19%;
|
|
2373
|
-
margin: 58% auto 0;
|
|
2374
|
-
width: 42%;
|
|
2375
|
-
}
|
|
2376
|
-
|
|
2377
|
-
.commerce-eyebrow {
|
|
2378
|
-
color: #00624b;
|
|
2379
|
-
font-size: 0.85rem;
|
|
2380
|
-
font-weight: 850;
|
|
2381
|
-
letter-spacing: 0.16rem;
|
|
2382
|
-
text-transform: uppercase;
|
|
2383
|
-
}
|
|
2384
|
-
|
|
2385
|
-
.commerce-title {
|
|
2386
|
-
font-size: clamp(2.5rem, 6vw, 4.8rem);
|
|
2387
|
-
line-height: 0.95;
|
|
2388
|
-
margin: 0.65rem 0 1.4rem;
|
|
2389
|
-
}
|
|
2390
|
-
|
|
2391
|
-
.commerce-lede {
|
|
2392
|
-
color: #555149;
|
|
2393
|
-
font-size: 1.2rem;
|
|
2394
|
-
line-height: 1.65;
|
|
2395
|
-
max-width: 42rem;
|
|
2396
|
-
}
|
|
2397
|
-
|
|
2398
|
-
.commerce-facts {
|
|
2399
|
-
display: grid;
|
|
2400
|
-
gap: 1rem;
|
|
2401
|
-
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
2402
|
-
margin: 2rem 0;
|
|
2403
|
-
}
|
|
2404
|
-
|
|
2405
|
-
.commerce-fact,
|
|
2406
|
-
.commerce-card,
|
|
2407
|
-
.commerce-cart-panel {
|
|
2408
|
-
background: rgba(255, 255, 255, 0.92);
|
|
2409
|
-
border-radius: 1rem;
|
|
2410
|
-
box-shadow: 0 0.5rem 1.25rem rgba(25, 20, 12, 0.08);
|
|
2411
|
-
padding: 1.25rem;
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
.commerce-fact span,
|
|
2415
|
-
.commerce-card span {
|
|
2416
|
-
color: #767067;
|
|
2417
|
-
display: block;
|
|
2418
|
-
font-weight: 750;
|
|
2419
|
-
margin-bottom: 0.45rem;
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
.commerce-fact strong {
|
|
2423
|
-
font-size: 1.1rem;
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
.commerce-checkout {
|
|
2427
|
-
align-items: center;
|
|
2428
|
-
display: flex;
|
|
2429
|
-
flex-wrap: wrap;
|
|
2430
|
-
gap: 0.75rem;
|
|
2431
|
-
}
|
|
2432
|
-
|
|
2433
|
-
.commerce-section-title {
|
|
2434
|
-
font-size: 1.8rem;
|
|
2435
|
-
margin: 4.5rem 0 1.5rem;
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
.commerce-grid {
|
|
2439
|
-
display: grid;
|
|
2440
|
-
gap: 1rem;
|
|
2441
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2442
|
-
}
|
|
2443
|
-
|
|
2444
|
-
.commerce-card strong {
|
|
2445
|
-
display: block;
|
|
2446
|
-
font-size: 1.45rem;
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
.commerce-cart-panel {
|
|
2450
|
-
margin-top: 2rem;
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
.commerce-cart-line {
|
|
2454
|
-
align-items: center;
|
|
2455
|
-
border-top: 0.0625rem solid rgba(23, 23, 23, 0.12);
|
|
2456
|
-
display: grid;
|
|
2457
|
-
gap: 1rem;
|
|
2458
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
2459
|
-
padding: 1rem 0;
|
|
2460
|
-
}
|
|
2461
|
-
|
|
2462
|
-
.commerce-cart-line:first-of-type {
|
|
2463
|
-
border-top: 0;
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
.commerce-quantity {
|
|
2467
|
-
align-items: center;
|
|
2468
|
-
display: flex;
|
|
2469
|
-
gap: 0.45rem;
|
|
2470
|
-
}
|
|
2471
|
-
|
|
2472
|
-
.commerce-quantity-button {
|
|
2473
|
-
min-height: 2rem;
|
|
2474
|
-
min-width: 2rem;
|
|
2475
|
-
padding: 0.25rem;
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
.commerce-boundary-toggle {
|
|
2479
|
-
align-items: center;
|
|
2480
|
-
background: rgba(255, 255, 255, 0.92);
|
|
2481
|
-
border-radius: 0.8rem;
|
|
2482
|
-
bottom: 1.5rem;
|
|
2483
|
-
box-shadow: 0 0.75rem 2rem rgba(18, 15, 10, 0.14);
|
|
2484
|
-
display: flex;
|
|
2485
|
-
gap: 0.65rem;
|
|
2486
|
-
left: 1.5rem;
|
|
2487
|
-
padding: 0.8rem 1rem;
|
|
2488
|
-
position: fixed;
|
|
2489
|
-
z-index: 80;
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
.commerce-boundary-toggle input {
|
|
2493
|
-
accent-color: #00624b;
|
|
2494
|
-
height: 1rem;
|
|
2495
|
-
width: 1rem;
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
@media (max-width: 860px) {
|
|
2499
|
-
.commerce-header,
|
|
2500
|
-
.commerce-footer,
|
|
2501
|
-
.commerce-product,
|
|
2502
|
-
.commerce-grid,
|
|
2503
|
-
.commerce-facts {
|
|
2504
|
-
grid-template-columns: 1fr;
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
|
-
.commerce-header,
|
|
2508
|
-
.commerce-footer {
|
|
2509
|
-
align-items: flex-start;
|
|
2510
|
-
flex-direction: column;
|
|
2511
|
-
}
|
|
2512
|
-
|
|
2513
|
-
.commerce-product-media {
|
|
2514
|
-
min-height: 20rem;
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
`;
|
|
2519
|
-
return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}
|
|
2520
|
-
|
|
2521
|
-
@layer ultramodern-remote-${app.domain ?? app.id} {
|
|
2522
|
-
[data-app-id="${app.id}"] {
|
|
2523
|
-
color: var(--um-color-foreground);
|
|
2524
|
-
background: var(--um-color-surface);
|
|
2525
|
-
font-family:
|
|
2526
|
-
Geist,
|
|
2527
|
-
Inter,
|
|
2528
|
-
ui-sans-serif,
|
|
2529
|
-
system-ui,
|
|
2530
|
-
-apple-system,
|
|
2531
|
-
BlinkMacSystemFont,
|
|
2532
|
-
"Segoe UI",
|
|
2533
|
-
sans-serif;
|
|
2534
|
-
min-height: 100vh;
|
|
2535
|
-
}
|
|
2536
|
-
|
|
2537
|
-
[data-app-id="${app.id}"] main {
|
|
2538
|
-
min-height: 100vh;
|
|
2539
|
-
padding: 2rem;
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
[data-app-id="${app.id}"] nav {
|
|
2543
|
-
display: flex;
|
|
2544
|
-
gap: 0.75rem;
|
|
2545
|
-
margin-bottom: 2rem;
|
|
2546
|
-
}
|
|
2547
|
-
|
|
2548
|
-
[data-app-id="${app.id}"] a {
|
|
2549
|
-
color: var(--um-color-link);
|
|
2550
|
-
}
|
|
2551
|
-
|
|
2552
|
-
[data-mf-remote="${app.id}"] {
|
|
2553
|
-
border: 0.0625rem solid color-mix(in srgb, var(--um-color-accent) 30%, transparent);
|
|
2554
|
-
border-radius: 0.5rem;
|
|
2555
|
-
padding: 1rem;
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
`;
|
|
2559
|
-
}
|
|
2560
|
-
function createServiceStyles(enableTailwind, scope, service) {
|
|
2561
|
-
return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}
|
|
2562
|
-
|
|
2563
|
-
@layer ultramodern-effect-service {
|
|
2564
|
-
[data-app-id="${service.id}"] {
|
|
2565
|
-
color: var(--um-color-foreground);
|
|
2566
|
-
background: var(--um-color-surface);
|
|
2567
|
-
font-family:
|
|
2568
|
-
Geist,
|
|
2569
|
-
Inter,
|
|
2570
|
-
ui-sans-serif,
|
|
2571
|
-
system-ui,
|
|
2572
|
-
-apple-system,
|
|
2573
|
-
BlinkMacSystemFont,
|
|
2574
|
-
"Segoe UI",
|
|
2575
|
-
sans-serif;
|
|
2576
|
-
min-height: 100vh;
|
|
2577
|
-
}
|
|
2578
|
-
|
|
2579
|
-
[data-app-id="${service.id}"] main {
|
|
2580
|
-
min-height: 100vh;
|
|
2581
|
-
padding: 2rem;
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
`;
|
|
2090
|
+
function createServiceStyles(enableTailwind, scope, _service) {
|
|
2091
|
+
return `${enableTailwind ? "@import 'tailwindcss';\n" : ''}${createCssTokenImport(scope)}`;
|
|
2585
2092
|
}
|
|
2586
2093
|
function createAppStyles(enableTailwind, scope, app) {
|
|
2587
2094
|
return 'shell' === app.kind ? createShellStyles(enableTailwind, scope) : createRemoteStyles(enableTailwind, scope, app);
|
|
@@ -2602,6 +2109,86 @@ export default {
|
|
|
2602
2109
|
} satisfies Config;
|
|
2603
2110
|
`;
|
|
2604
2111
|
}
|
|
2112
|
+
function createCommerceAssetSvg(title, palette) {
|
|
2113
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="900" viewBox="0 0 1440 900" role="img" aria-label="${title}">
|
|
2114
|
+
<defs>
|
|
2115
|
+
<linearGradient id="sky" x1="0" x2="0" y1="0" y2="1">
|
|
2116
|
+
<stop offset="0" stop-color="${palette.sky}"/>
|
|
2117
|
+
<stop offset="1" stop-color="#fff8dc"/>
|
|
2118
|
+
</linearGradient>
|
|
2119
|
+
<linearGradient id="field" x1="0" x2="1" y1="0" y2="1">
|
|
2120
|
+
<stop offset="0" stop-color="${palette.ground}"/>
|
|
2121
|
+
<stop offset="1" stop-color="${palette.accent}"/>
|
|
2122
|
+
</linearGradient>
|
|
2123
|
+
</defs>
|
|
2124
|
+
<rect width="1440" height="900" fill="url(#sky)"/>
|
|
2125
|
+
<path d="M0 566c172-78 330-102 474-72 125 26 219 91 340 106 170 21 339-74 626-43v343H0z" fill="url(#field)"/>
|
|
2126
|
+
<path d="M0 686c205-70 451-66 738 12 287 77 521 66 702-33v235H0z" fill="#4f7f38" opacity=".55"/>
|
|
2127
|
+
<g fill="none" stroke="#fff6b7" stroke-linecap="round" stroke-width="10" opacity=".55">
|
|
2128
|
+
<path d="M108 820c205-138 382-202 530-192"/>
|
|
2129
|
+
<path d="M322 862c176-134 338-198 486-193"/>
|
|
2130
|
+
<path d="M583 886c119-121 260-190 422-207"/>
|
|
2131
|
+
<path d="M868 878c95-94 207-153 336-176"/>
|
|
2132
|
+
</g>
|
|
2133
|
+
<g transform="translate(430 430)">
|
|
2134
|
+
<circle cx="170" cy="210" r="92" fill="#161616"/>
|
|
2135
|
+
<circle cx="170" cy="210" r="54" fill="#d7c46d"/>
|
|
2136
|
+
<circle cx="514" cy="214" r="108" fill="#161616"/>
|
|
2137
|
+
<circle cx="514" cy="214" r="63" fill="#d7c46d"/>
|
|
2138
|
+
<path d="M194 142h194l72-100h114c49 0 89 39 89 88v57H625l-51-90H452l-78 114H209z" fill="${palette.tractor}"/>
|
|
2139
|
+
<path d="M283 67h134l-54 73H249z" fill="#c9ecff" opacity=".72"/>
|
|
2140
|
+
<path d="M120 184h430v54H120z" fill="${palette.tractor}"/>
|
|
2141
|
+
<path d="M578 52l60-35M618 37l34 72" stroke="#171717" stroke-linecap="round" stroke-width="14"/>
|
|
2142
|
+
<path d="M90 236h578" stroke="#171717" stroke-linecap="round" stroke-width="18"/>
|
|
2143
|
+
</g>
|
|
2144
|
+
</svg>
|
|
2145
|
+
`;
|
|
2146
|
+
}
|
|
2147
|
+
function commerceAssetsForApp(app) {
|
|
2148
|
+
if ('shell' === app.kind) return {
|
|
2149
|
+
'src/assets/hero-field.svg': createCommerceAssetSvg('Tractor crossing cultivated fields', {
|
|
2150
|
+
accent: '#d6b85d',
|
|
2151
|
+
ground: '#84ad58',
|
|
2152
|
+
sky: '#9fd6e8',
|
|
2153
|
+
tractor: '#005f73'
|
|
2154
|
+
})
|
|
2155
|
+
};
|
|
2156
|
+
if ('remote-explore' === app.id) return {
|
|
2157
|
+
'src/assets/autonomy.svg': createCommerceAssetSvg('Autonomous tractor concept', {
|
|
2158
|
+
accent: '#c26a2e',
|
|
2159
|
+
ground: '#668f55',
|
|
2160
|
+
sky: '#d5e7de',
|
|
2161
|
+
tractor: '#f2a51a'
|
|
2162
|
+
}),
|
|
2163
|
+
'src/assets/field-loader.svg': createCommerceAssetSvg('Field Loader 112 tractor', {
|
|
2164
|
+
accent: '#d6b85d',
|
|
2165
|
+
ground: '#84ad58',
|
|
2166
|
+
sky: '#9fd6e8',
|
|
2167
|
+
tractor: '#00624b'
|
|
2168
|
+
}),
|
|
2169
|
+
'src/assets/orchard.svg': createCommerceAssetSvg('Orchard tractor between tight rows', {
|
|
2170
|
+
accent: '#b45b2d',
|
|
2171
|
+
ground: '#6f9b4a',
|
|
2172
|
+
sky: '#c9ebff',
|
|
2173
|
+
tractor: '#1d5d9b'
|
|
2174
|
+
}),
|
|
2175
|
+
'src/assets/vineyard.svg': createCommerceAssetSvg('Vineyard narrow tractor', {
|
|
2176
|
+
accent: '#b88d58',
|
|
2177
|
+
ground: '#5e8a45',
|
|
2178
|
+
sky: '#f1dcb9',
|
|
2179
|
+
tractor: '#914d76'
|
|
2180
|
+
})
|
|
2181
|
+
};
|
|
2182
|
+
if ('remote-decide' === app.id) return {
|
|
2183
|
+
'src/assets/field-loader.svg': createCommerceAssetSvg('Field Loader 112 tractor detail', {
|
|
2184
|
+
accent: '#d6b85d',
|
|
2185
|
+
ground: '#84ad58',
|
|
2186
|
+
sky: '#9fd6e8',
|
|
2187
|
+
tractor: '#00624b'
|
|
2188
|
+
})
|
|
2189
|
+
};
|
|
2190
|
+
return {};
|
|
2191
|
+
}
|
|
2605
2192
|
function createLocalizedHeadComponent() {
|
|
2606
2193
|
return `const fallbackLanguage = 'en';
|
|
2607
2194
|
const supportedLanguages = ['en', 'cs'] as const;
|
|
@@ -2756,302 +2343,269 @@ function createShellPage() {
|
|
|
2756
2343
|
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2757
2344
|
import { Helmet } from '@modern-js/runtime/head';
|
|
2758
2345
|
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2759
|
-
import
|
|
2760
|
-
import
|
|
2346
|
+
import heroField from '../../assets/hero-field.svg';
|
|
2347
|
+
import ShellFrame from '../shell-frame';
|
|
2348
|
+
import { StorePicker } from '../remote-components';
|
|
2761
2349
|
import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
|
|
2762
2350
|
import { ultramodernUiMarker } from '../../ultramodern-build';
|
|
2763
2351
|
|
|
2764
|
-
const languageCodes = ['en', 'cs'] as const;
|
|
2765
|
-
|
|
2766
2352
|
${createLocalizedHeadComponent()}
|
|
2767
|
-
type HomeRouteRemotes = {
|
|
2768
|
-
Header: ComponentType;
|
|
2769
|
-
MiniCart: ComponentType;
|
|
2770
|
-
StorePicker: ComponentType;
|
|
2771
|
-
};
|
|
2772
|
-
|
|
2773
|
-
const useHomeRouteRemotes = () => {
|
|
2774
|
-
const [remotes, setRemotes] = useState<HomeRouteRemotes | null>(null);
|
|
2775
|
-
|
|
2776
|
-
useEffect(() => {
|
|
2777
|
-
let mounted = true;
|
|
2778
|
-
import('../remote-components').then(({ Header, MiniCart, StorePicker }) => {
|
|
2779
|
-
if (mounted) {
|
|
2780
|
-
setRemotes({ Header, MiniCart, StorePicker });
|
|
2781
|
-
}
|
|
2782
|
-
});
|
|
2783
|
-
|
|
2784
|
-
return () => {
|
|
2785
|
-
mounted = false;
|
|
2786
|
-
};
|
|
2787
|
-
}, []);
|
|
2788
|
-
|
|
2789
|
-
return remotes;
|
|
2790
|
-
};
|
|
2791
|
-
|
|
2792
2353
|
export default function ShellHome() {
|
|
2793
2354
|
const { i18nInstance, language } = useModernI18n();
|
|
2794
2355
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2795
|
-
const location = useLocation();
|
|
2796
|
-
const suffix = locationSuffix(location);
|
|
2797
|
-
const remotes = useHomeRouteRemotes();
|
|
2798
|
-
const Header = remotes?.Header;
|
|
2799
|
-
const MiniCart = remotes?.MiniCart;
|
|
2800
|
-
const StorePicker = remotes?.StorePicker;
|
|
2801
2356
|
|
|
2802
2357
|
return (
|
|
2803
|
-
<
|
|
2358
|
+
<ShellFrame>
|
|
2804
2359
|
<LocalizedHead />
|
|
2805
|
-
<
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
className="commerce-pill"
|
|
2813
|
-
href={\`\${localizedPath(location.pathname, code)}\${suffix}\`}
|
|
2814
|
-
key={code}
|
|
2815
|
-
>
|
|
2816
|
-
{t(\`shell.language.\${code}\`)}
|
|
2817
|
-
</a>
|
|
2818
|
-
))}
|
|
2819
|
-
</nav>
|
|
2820
|
-
{MiniCart ? <MiniCart /> : null}
|
|
2821
|
-
</div>
|
|
2822
|
-
<section className="commerce-page commerce-hero">
|
|
2823
|
-
<p className="commerce-eyebrow">{t('shell.hero.eyebrow')}</p>
|
|
2824
|
-
<h1 className="commerce-title">{t('shell.title')}</h1>
|
|
2825
|
-
<p className="commerce-lede">{t('shell.hero.lede')}</p>
|
|
2826
|
-
<div className="commerce-checkout">
|
|
2827
|
-
<a className="commerce-button" href={\`/\${language}/tractors/field-loader-112\`}>
|
|
2360
|
+
<section className="mx-auto grid max-w-7xl items-center gap-8 py-8 md:grid-cols-[0.9fr_1.1fr] lg:gap-14">
|
|
2361
|
+
<div className="min-w-0">
|
|
2362
|
+
<p className="text-xs font-black uppercase tracking-[0.18em] text-emerald-800">{t('shell.hero.eyebrow')}</p>
|
|
2363
|
+
<h1 className="mt-3 max-w-3xl text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl">{t('shell.title')}</h1>
|
|
2364
|
+
<p className="mt-5 max-w-2xl text-lg leading-8 text-stone-600">{t('shell.hero.lede')}</p>
|
|
2365
|
+
<div className="mt-7 flex flex-wrap gap-3">
|
|
2366
|
+
<a className="inline-flex min-h-11 items-center justify-center rounded-full bg-emerald-800 px-5 font-bold text-white shadow-lg shadow-stone-900/10" href={\`/\${language}/tractors/field-loader-112\`}>
|
|
2828
2367
|
{t('shell.hero.primary')}
|
|
2829
|
-
|
|
2830
|
-
|
|
2368
|
+
</a>
|
|
2369
|
+
<a className="inline-flex min-h-11 items-center justify-center rounded-full border border-stone-900/15 bg-white/90 px-5 font-bold text-stone-950 shadow-lg shadow-stone-900/10" href={\`/\${language}/tractors\`}>
|
|
2831
2370
|
{t('shell.hero.secondary')}
|
|
2832
|
-
|
|
2371
|
+
</a>
|
|
2372
|
+
</div>
|
|
2833
2373
|
</div>
|
|
2374
|
+
<img alt="" className="aspect-[16/10] w-full rounded-3xl bg-stone-200 object-cover shadow-2xl shadow-stone-900/20" src={heroField} />
|
|
2834
2375
|
</section>
|
|
2835
|
-
|
|
2836
|
-
<p data-testid="ultramodern-preset">presetUltramodern workspace</p>
|
|
2837
|
-
<p data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
|
|
2376
|
+
<StorePicker />
|
|
2377
|
+
<p className="sr-only" data-testid="ultramodern-preset">presetUltramodern workspace</p>
|
|
2378
|
+
<p className="sr-only" data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
|
|
2838
2379
|
{ultramodernUiMarker.appId}:{ultramodernUiMarker.version}
|
|
2839
2380
|
</p>
|
|
2840
|
-
</
|
|
2381
|
+
</ShellFrame>
|
|
2841
2382
|
);
|
|
2842
2383
|
}
|
|
2843
2384
|
`;
|
|
2844
2385
|
}
|
|
2845
2386
|
function createShellTractorsPage() {
|
|
2846
|
-
return `import {
|
|
2847
|
-
import { Helmet } from '@modern-js/runtime/head';
|
|
2387
|
+
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2848
2388
|
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2849
|
-
import
|
|
2850
|
-
import
|
|
2389
|
+
import ShellFrame from '../../shell-frame';
|
|
2390
|
+
import { Recommendations } from '../../remote-components';
|
|
2851
2391
|
import { ultramodernLocalisedUrls } from '../../ultramodern-route-metadata';
|
|
2852
2392
|
|
|
2853
|
-
const languageCodes = ['en', 'cs'] as const;
|
|
2854
|
-
|
|
2855
2393
|
${createLocalizedHeadComponent()}
|
|
2856
|
-
type TractorsRouteRemotes = {
|
|
2857
|
-
Header: ComponentType;
|
|
2858
|
-
MiniCart: ComponentType;
|
|
2859
|
-
Recommendations: ComponentType;
|
|
2860
|
-
};
|
|
2861
|
-
|
|
2862
|
-
const useTractorsRouteRemotes = () => {
|
|
2863
|
-
const [remotes, setRemotes] = useState<TractorsRouteRemotes | null>(null);
|
|
2864
|
-
|
|
2865
|
-
useEffect(() => {
|
|
2866
|
-
let mounted = true;
|
|
2867
|
-
import('../../remote-components').then(({ Header, MiniCart, Recommendations }) => {
|
|
2868
|
-
if (mounted) {
|
|
2869
|
-
setRemotes({ Header, MiniCart, Recommendations });
|
|
2870
|
-
}
|
|
2871
|
-
});
|
|
2872
|
-
|
|
2873
|
-
return () => {
|
|
2874
|
-
mounted = false;
|
|
2875
|
-
};
|
|
2876
|
-
}, []);
|
|
2877
|
-
|
|
2878
|
-
return remotes;
|
|
2879
|
-
};
|
|
2880
|
-
|
|
2881
2394
|
export default function ShellTractorsPage() {
|
|
2882
|
-
const { i18nInstance, language } = useModernI18n();
|
|
2883
|
-
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2884
|
-
const location = useLocation();
|
|
2885
|
-
const suffix = locationSuffix(location);
|
|
2886
|
-
const remotes = useTractorsRouteRemotes();
|
|
2887
|
-
const Header = remotes?.Header;
|
|
2888
|
-
const MiniCart = remotes?.MiniCart;
|
|
2889
|
-
const Recommendations = remotes?.Recommendations;
|
|
2890
|
-
|
|
2891
2395
|
return (
|
|
2892
|
-
<
|
|
2396
|
+
<ShellFrame>
|
|
2893
2397
|
<LocalizedHead />
|
|
2894
|
-
<
|
|
2895
|
-
|
|
2896
|
-
<div className="commerce-shell-actions">
|
|
2897
|
-
<nav aria-label={t('shell.language.switcher')} className="commerce-language">
|
|
2898
|
-
{languageCodes.map(code => (
|
|
2899
|
-
<a
|
|
2900
|
-
aria-current={language === code ? 'page' : undefined}
|
|
2901
|
-
className="commerce-pill"
|
|
2902
|
-
href={\`\${localizedPath(location.pathname, code)}\${suffix}\`}
|
|
2903
|
-
key={code}
|
|
2904
|
-
>
|
|
2905
|
-
{t(\`shell.language.\${code}\`)}
|
|
2906
|
-
</a>
|
|
2907
|
-
))}
|
|
2908
|
-
</nav>
|
|
2909
|
-
{MiniCart ? <MiniCart /> : null}
|
|
2910
|
-
</div>
|
|
2911
|
-
{Recommendations ? <Recommendations /> : null}
|
|
2912
|
-
</main>
|
|
2398
|
+
<Recommendations />
|
|
2399
|
+
</ShellFrame>
|
|
2913
2400
|
);
|
|
2914
2401
|
}
|
|
2915
2402
|
`;
|
|
2916
2403
|
}
|
|
2917
2404
|
function createShellProductPage() {
|
|
2918
|
-
return `import {
|
|
2919
|
-
import { Helmet } from '@modern-js/runtime/head';
|
|
2405
|
+
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2920
2406
|
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2921
|
-
import
|
|
2922
|
-
import
|
|
2407
|
+
import ShellFrame from '../../../shell-frame';
|
|
2408
|
+
import { ProductPage } from '../../../remote-components';
|
|
2923
2409
|
import { ultramodernLocalisedUrls } from '../../../ultramodern-route-metadata';
|
|
2924
2410
|
|
|
2925
|
-
const languageCodes = ['en', 'cs'] as const;
|
|
2926
|
-
|
|
2927
2411
|
${createLocalizedHeadComponent()}
|
|
2928
|
-
type ProductRouteRemotes = {
|
|
2929
|
-
Header: ComponentType;
|
|
2930
|
-
MiniCart: ComponentType;
|
|
2931
|
-
ProductPage: ComponentType;
|
|
2932
|
-
};
|
|
2933
|
-
|
|
2934
|
-
const useProductRouteRemotes = () => {
|
|
2935
|
-
const [remotes, setRemotes] = useState<ProductRouteRemotes | null>(null);
|
|
2936
|
-
|
|
2937
|
-
useEffect(() => {
|
|
2938
|
-
let mounted = true;
|
|
2939
|
-
import('../../../remote-components').then(
|
|
2940
|
-
({ Header, MiniCart, ProductPage }) => {
|
|
2941
|
-
if (mounted) {
|
|
2942
|
-
setRemotes({ Header, MiniCart, ProductPage });
|
|
2943
|
-
}
|
|
2944
|
-
},
|
|
2945
|
-
);
|
|
2946
|
-
|
|
2947
|
-
return () => {
|
|
2948
|
-
mounted = false;
|
|
2949
|
-
};
|
|
2950
|
-
}, []);
|
|
2951
|
-
|
|
2952
|
-
return remotes;
|
|
2953
|
-
};
|
|
2954
|
-
|
|
2955
2412
|
export default function ShellProductPage() {
|
|
2956
|
-
const { i18nInstance, language } = useModernI18n();
|
|
2957
|
-
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2958
|
-
const location = useLocation();
|
|
2959
|
-
const suffix = locationSuffix(location);
|
|
2960
|
-
const remotes = useProductRouteRemotes();
|
|
2961
|
-
const Header = remotes?.Header;
|
|
2962
|
-
const MiniCart = remotes?.MiniCart;
|
|
2963
|
-
const ProductPage = remotes?.ProductPage;
|
|
2964
|
-
|
|
2965
2413
|
return (
|
|
2966
|
-
<
|
|
2414
|
+
<ShellFrame>
|
|
2967
2415
|
<LocalizedHead />
|
|
2968
|
-
<
|
|
2969
|
-
|
|
2970
|
-
<div className="commerce-shell-actions">
|
|
2971
|
-
<nav aria-label={t('shell.language.switcher')} className="commerce-language">
|
|
2972
|
-
{languageCodes.map(code => (
|
|
2973
|
-
<a
|
|
2974
|
-
aria-current={language === code ? 'page' : undefined}
|
|
2975
|
-
className="commerce-pill"
|
|
2976
|
-
href={\`\${localizedPath(location.pathname, code)}\${suffix}\`}
|
|
2977
|
-
key={code}
|
|
2978
|
-
>
|
|
2979
|
-
{t(\`shell.language.\${code}\`)}
|
|
2980
|
-
</a>
|
|
2981
|
-
))}
|
|
2982
|
-
</nav>
|
|
2983
|
-
{MiniCart ? <MiniCart /> : null}
|
|
2984
|
-
</div>
|
|
2985
|
-
{ProductPage ? <ProductPage /> : null}
|
|
2986
|
-
</main>
|
|
2416
|
+
<ProductPage />
|
|
2417
|
+
</ShellFrame>
|
|
2987
2418
|
);
|
|
2988
2419
|
}
|
|
2989
2420
|
`;
|
|
2990
2421
|
}
|
|
2991
2422
|
function createShellCartPage() {
|
|
2992
|
-
return `import {
|
|
2993
|
-
import { Helmet } from '@modern-js/runtime/head';
|
|
2423
|
+
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2994
2424
|
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2995
|
-
import
|
|
2996
|
-
import
|
|
2425
|
+
import ShellFrame from '../../shell-frame';
|
|
2426
|
+
import { CartPage } from '../../remote-components';
|
|
2997
2427
|
import { ultramodernLocalisedUrls } from '../../ultramodern-route-metadata';
|
|
2998
2428
|
|
|
2999
|
-
const languageCodes = ['en', 'cs'] as const;
|
|
3000
|
-
|
|
3001
2429
|
${createLocalizedHeadComponent()}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
2430
|
+
export default function ShellCartPage() {
|
|
2431
|
+
return (
|
|
2432
|
+
<ShellFrame showCart={false}>
|
|
2433
|
+
<LocalizedHead />
|
|
2434
|
+
<CartPage />
|
|
2435
|
+
</ShellFrame>
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
2438
|
+
`;
|
|
2439
|
+
}
|
|
2440
|
+
function createShellFrameComponent() {
|
|
2441
|
+
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2442
|
+
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2443
|
+
import type { ReactNode } from 'react';
|
|
2444
|
+
import BoundaryOverlay from './boundary-overlay';
|
|
2445
|
+
import { Header, MiniCart } from './remote-components';
|
|
2446
|
+
import { ultramodernLocalisedUrls } from './ultramodern-route-metadata';
|
|
2447
|
+
|
|
2448
|
+
const supportedLanguages = ['en', 'cs'] as const;
|
|
2449
|
+
type SupportedLanguage = (typeof supportedLanguages)[number];
|
|
2450
|
+
|
|
2451
|
+
type ShellFrameProps = {
|
|
2452
|
+
children: ReactNode;
|
|
2453
|
+
showCart?: boolean;
|
|
3005
2454
|
};
|
|
3006
2455
|
|
|
3007
|
-
const
|
|
3008
|
-
|
|
2456
|
+
const localisedUrls = ultramodernLocalisedUrls as Record<
|
|
2457
|
+
string,
|
|
2458
|
+
Record<SupportedLanguage, string>
|
|
2459
|
+
>;
|
|
3009
2460
|
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
2461
|
+
const isSupportedLanguage = (value: string): value is SupportedLanguage =>
|
|
2462
|
+
supportedLanguages.includes(value as SupportedLanguage);
|
|
2463
|
+
|
|
2464
|
+
const normalisePath = (pathname: string) => {
|
|
2465
|
+
const normalised = pathname.replace(/\\/+$/u, '').replace(/\\/+/gu, '/');
|
|
2466
|
+
return normalised.length > 0 ? normalised : '/';
|
|
2467
|
+
};
|
|
2468
|
+
|
|
2469
|
+
const stripLanguagePrefix = (pathname: string) => {
|
|
2470
|
+
const segments = normalisePath(pathname).split('/').filter(Boolean);
|
|
2471
|
+
if (segments.length > 0 && isSupportedLanguage(segments[0] ?? '')) {
|
|
2472
|
+
segments.shift();
|
|
2473
|
+
}
|
|
2474
|
+
return \`/\${segments.join('/')}\`;
|
|
2475
|
+
};
|
|
2476
|
+
|
|
2477
|
+
const escapeRegExp = (value: string) =>
|
|
2478
|
+
value.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&');
|
|
2479
|
+
|
|
2480
|
+
const paramName = (segment: string) => segment.slice(1).replace(/\\?$/u, '');
|
|
2481
|
+
|
|
2482
|
+
const matchPattern = (pathname: string, pattern: string) => {
|
|
2483
|
+
const names: string[] = [];
|
|
2484
|
+
const source = normalisePath(pattern)
|
|
2485
|
+
.split('/')
|
|
2486
|
+
.filter(Boolean)
|
|
2487
|
+
.map(segment => {
|
|
2488
|
+
if (segment.startsWith(':')) {
|
|
2489
|
+
names.push(paramName(segment));
|
|
2490
|
+
return segment.endsWith('?') ? '(?:/([^/]+))?' : '/([^/]+)';
|
|
3015
2491
|
}
|
|
3016
|
-
|
|
2492
|
+
return \`/\${escapeRegExp(segment)}\`;
|
|
2493
|
+
})
|
|
2494
|
+
.join('');
|
|
2495
|
+
const match = new RegExp(\`^\${source || '/'}$\`).exec(normalisePath(pathname));
|
|
3017
2496
|
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
}, []);
|
|
2497
|
+
if (!match) {
|
|
2498
|
+
return undefined;
|
|
2499
|
+
}
|
|
3022
2500
|
|
|
3023
|
-
return
|
|
2501
|
+
return names.reduce<Record<string, string>>((params, name, index) => {
|
|
2502
|
+
params[name] = decodeURIComponent(match[index + 1] ?? '');
|
|
2503
|
+
return params;
|
|
2504
|
+
}, {});
|
|
3024
2505
|
};
|
|
3025
2506
|
|
|
3026
|
-
|
|
2507
|
+
const buildPath = (pattern: string, params: Record<string, string>) => {
|
|
2508
|
+
const path = normalisePath(pattern)
|
|
2509
|
+
.split('/')
|
|
2510
|
+
.filter(Boolean)
|
|
2511
|
+
.map(segment => {
|
|
2512
|
+
if (!segment.startsWith(':')) {
|
|
2513
|
+
return segment;
|
|
2514
|
+
}
|
|
2515
|
+
const value = params[paramName(segment)];
|
|
2516
|
+
return value ? encodeURIComponent(value) : '';
|
|
2517
|
+
})
|
|
2518
|
+
.filter(Boolean)
|
|
2519
|
+
.join('/');
|
|
2520
|
+
|
|
2521
|
+
return \`/\${path}\`;
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2524
|
+
const resolveLocalisedPath = (
|
|
2525
|
+
pathname: string,
|
|
2526
|
+
targetLanguage: SupportedLanguage,
|
|
2527
|
+
) => {
|
|
2528
|
+
const pathWithoutLanguage = stripLanguagePrefix(pathname);
|
|
2529
|
+
|
|
2530
|
+
for (const entry of Object.values(localisedUrls)) {
|
|
2531
|
+
const targetPattern = entry[targetLanguage];
|
|
2532
|
+
if (!targetPattern) {
|
|
2533
|
+
continue;
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
for (const language of supportedLanguages) {
|
|
2537
|
+
const sourcePattern = entry[language];
|
|
2538
|
+
const params = sourcePattern
|
|
2539
|
+
? matchPattern(pathWithoutLanguage, sourcePattern)
|
|
2540
|
+
: undefined;
|
|
2541
|
+
if (params) {
|
|
2542
|
+
return buildPath(targetPattern, params);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
return pathWithoutLanguage;
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2550
|
+
const localizedPath = (pathname: string, language: SupportedLanguage) => {
|
|
2551
|
+
const pathWithoutLanguage = resolveLocalisedPath(pathname, language);
|
|
2552
|
+
return pathWithoutLanguage === '/' ? \`/\${language}\` : \`/\${language}\${pathWithoutLanguage}\`;
|
|
2553
|
+
};
|
|
2554
|
+
|
|
2555
|
+
const locationSuffix = (location: {
|
|
2556
|
+
hash?: unknown;
|
|
2557
|
+
search?: unknown;
|
|
2558
|
+
searchStr?: unknown;
|
|
2559
|
+
}) => {
|
|
2560
|
+
const locationSearch =
|
|
2561
|
+
typeof location.searchStr === 'string'
|
|
2562
|
+
? location.searchStr
|
|
2563
|
+
: typeof location.search === 'string'
|
|
2564
|
+
? location.search
|
|
2565
|
+
: '';
|
|
2566
|
+
const locationHash = typeof location.hash === 'string' ? location.hash : '';
|
|
2567
|
+
|
|
2568
|
+
return \`\${locationSearch}\${locationHash}\`;
|
|
2569
|
+
};
|
|
2570
|
+
|
|
2571
|
+
export default function ShellFrame({ children, showCart = true }: ShellFrameProps) {
|
|
3027
2572
|
const { i18nInstance, language } = useModernI18n();
|
|
3028
2573
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3029
2574
|
const location = useLocation();
|
|
3030
2575
|
const suffix = locationSuffix(location);
|
|
3031
|
-
const remotes = useCartRouteRemotes();
|
|
3032
|
-
const Header = remotes?.Header;
|
|
3033
|
-
const CartPage = remotes?.CartPage;
|
|
3034
2576
|
|
|
3035
2577
|
return (
|
|
3036
|
-
<main className="
|
|
3037
|
-
<LocalizedHead />
|
|
2578
|
+
<main className="min-h-screen bg-um-canvas px-4 py-5 text-um-foreground sm:px-6 lg:px-12">
|
|
3038
2579
|
<BoundaryOverlay />
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
<
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
2580
|
+
<div className="mx-auto flex min-h-20 max-w-7xl flex-wrap items-center justify-between gap-3 bg-white/90 px-4 py-3 shadow-xl shadow-stone-900/10 sm:px-6">
|
|
2581
|
+
<Header />
|
|
2582
|
+
<div className="ml-auto flex min-w-0 items-center gap-2">
|
|
2583
|
+
<label className="sr-only" htmlFor="ultramodern-language">
|
|
2584
|
+
{t('shell.language.switcher')}
|
|
2585
|
+
</label>
|
|
2586
|
+
<select
|
|
2587
|
+
className="h-10 rounded-full border border-stone-900/15 bg-white px-3 text-sm font-extrabold text-stone-950 shadow-lg shadow-stone-900/5"
|
|
2588
|
+
id="ultramodern-language"
|
|
2589
|
+
onChange={event => {
|
|
2590
|
+
const nextLanguage = event.currentTarget.value;
|
|
2591
|
+
if (isSupportedLanguage(nextLanguage)) {
|
|
2592
|
+
window.location.assign(
|
|
2593
|
+
\`\${localizedPath(location.pathname, nextLanguage)}\${suffix}\`,
|
|
2594
|
+
);
|
|
2595
|
+
}
|
|
2596
|
+
}}
|
|
2597
|
+
value={language}
|
|
2598
|
+
>
|
|
2599
|
+
{supportedLanguages.map(code => (
|
|
2600
|
+
<option key={code} value={code}>
|
|
2601
|
+
{t(\`shell.language.\${code}\`)}
|
|
2602
|
+
</option>
|
|
2603
|
+
))}
|
|
2604
|
+
</select>
|
|
2605
|
+
{showCart ? <MiniCart /> : null}
|
|
2606
|
+
</div>
|
|
3053
2607
|
</div>
|
|
3054
|
-
{
|
|
2608
|
+
{children}
|
|
3055
2609
|
</main>
|
|
3056
2610
|
);
|
|
3057
2611
|
}
|
|
@@ -3179,8 +2733,9 @@ export default function BoundaryOverlay() {
|
|
|
3179
2733
|
|
|
3180
2734
|
return (
|
|
3181
2735
|
<>
|
|
3182
|
-
<label className="
|
|
2736
|
+
<label className="fixed bottom-5 left-5 z-[80] flex items-center gap-2 rounded-xl border border-stone-900/10 bg-white/95 px-4 py-3 text-sm font-semibold text-stone-950 shadow-2xl shadow-stone-900/15">
|
|
3183
2737
|
<input
|
|
2738
|
+
className="size-4 accent-emerald-800"
|
|
3184
2739
|
checked={enabled}
|
|
3185
2740
|
onChange={event => setEnabled(event.currentTarget.checked)}
|
|
3186
2741
|
type="checkbox"
|
|
@@ -3188,15 +2743,16 @@ export default function BoundaryOverlay() {
|
|
|
3188
2743
|
<span>{toggleLabel}</span>
|
|
3189
2744
|
</label>
|
|
3190
2745
|
{enabled ? (
|
|
3191
|
-
<div aria-hidden="true" className="
|
|
2746
|
+
<div aria-hidden="true" className="pointer-events-none fixed inset-0 z-[70]">
|
|
3192
2747
|
{boxes.map(box => (
|
|
3193
2748
|
<div
|
|
3194
|
-
className="
|
|
2749
|
+
className="fixed rounded-lg border"
|
|
3195
2750
|
data-label-placement={box.labelPlacement}
|
|
3196
2751
|
key={box.id}
|
|
3197
2752
|
style={
|
|
3198
2753
|
{
|
|
3199
|
-
|
|
2754
|
+
borderColor: box.color,
|
|
2755
|
+
boxShadow: \`0 0 0 1px rgba(255,255,255,.72), 0 6px 20px color-mix(in srgb, \${box.color} 20%, transparent)\`,
|
|
3200
2756
|
height: box.height,
|
|
3201
2757
|
left: box.left,
|
|
3202
2758
|
top: box.top,
|
|
@@ -3204,7 +2760,12 @@ export default function BoundaryOverlay() {
|
|
|
3204
2760
|
} as CSSProperties
|
|
3205
2761
|
}
|
|
3206
2762
|
>
|
|
3207
|
-
<span
|
|
2763
|
+
<span
|
|
2764
|
+
className={\`absolute right-1 top-1 whitespace-nowrap rounded-full px-2 py-1 text-[0.7rem] font-black leading-none text-stone-950 \${box.labelPlacement === 'above' ? 'bottom-[calc(100%+0.25rem)] top-auto' : ''}\`}
|
|
2765
|
+
style={{ backgroundColor: box.color }}
|
|
2766
|
+
>
|
|
2767
|
+
{box.label}
|
|
2768
|
+
</span>
|
|
3208
2769
|
</div>
|
|
3209
2770
|
))}
|
|
3210
2771
|
</div>
|
|
@@ -3214,13 +2775,19 @@ export default function BoundaryOverlay() {
|
|
|
3214
2775
|
}
|
|
3215
2776
|
`;
|
|
3216
2777
|
}
|
|
3217
|
-
function createShellRemoteComponents() {
|
|
2778
|
+
function createShellRemoteComponents(scope) {
|
|
3218
2779
|
return `import { createLazyComponent } from '@module-federation/modern-js-v3/react';
|
|
3219
2780
|
import { getInstance, loadRemote } from '@module-federation/modern-js-v3/runtime';
|
|
3220
|
-
import type
|
|
2781
|
+
import { Suspense, useEffect, useMemo, useState, type ComponentType } from 'react';
|
|
2782
|
+
import HeaderServer from '${ultramodern_workspace_packageName(scope, 'remote-explore')}/Header';
|
|
2783
|
+
import StorePickerServer from '${ultramodern_workspace_packageName(scope, 'remote-explore')}/StorePicker';
|
|
2784
|
+
import RecommendationsServer from '${ultramodern_workspace_packageName(scope, 'remote-explore')}/Recommendations';
|
|
2785
|
+
import ProductPageServer from '${ultramodern_workspace_packageName(scope, 'remote-decide')}/ProductPage';
|
|
2786
|
+
import MiniCartServer from '${ultramodern_workspace_packageName(scope, 'remote-checkout')}/MiniCart';
|
|
2787
|
+
import CartPageServer from '${ultramodern_workspace_packageName(scope, 'remote-checkout')}/CartPage';
|
|
3221
2788
|
|
|
3222
2789
|
type RemoteComponentModule = {
|
|
3223
|
-
default:
|
|
2790
|
+
default: ComponentType;
|
|
3224
2791
|
};
|
|
3225
2792
|
|
|
3226
2793
|
const loadRemoteComponent = async (specifier: string) => {
|
|
@@ -3233,56 +2800,54 @@ const loadRemoteComponent = async (specifier: string) => {
|
|
|
3233
2800
|
|
|
3234
2801
|
const remoteFallback =
|
|
3235
2802
|
({ error }: { error: Error }) =>
|
|
3236
|
-
<div data-remote-error={error.name}>Remote unavailable</div>;
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
export const
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
noSSR: true,
|
|
3285
|
-
});
|
|
2803
|
+
<div className="rounded-xl border border-red-900/20 bg-red-50 px-4 py-3 text-sm font-semibold text-red-900" data-remote-error={error.name}>Remote unavailable</div>;
|
|
2804
|
+
|
|
2805
|
+
const createHydratedRemote = (
|
|
2806
|
+
ServerComponent: ComponentType,
|
|
2807
|
+
specifier: string,
|
|
2808
|
+
) => {
|
|
2809
|
+
return function HydratedRemote() {
|
|
2810
|
+
const [hydrated, setHydrated] = useState(false);
|
|
2811
|
+
|
|
2812
|
+
useEffect(() => {
|
|
2813
|
+
setHydrated(true);
|
|
2814
|
+
}, []);
|
|
2815
|
+
|
|
2816
|
+
const FederatedComponent = useMemo(() => {
|
|
2817
|
+
if (!hydrated) {
|
|
2818
|
+
return undefined;
|
|
2819
|
+
}
|
|
2820
|
+
const instance = getInstance();
|
|
2821
|
+
if (!instance) {
|
|
2822
|
+
return undefined;
|
|
2823
|
+
}
|
|
2824
|
+
return createLazyComponent({
|
|
2825
|
+
export: 'default',
|
|
2826
|
+
fallback: remoteFallback,
|
|
2827
|
+
instance,
|
|
2828
|
+
loader: () => loadRemoteComponent(specifier),
|
|
2829
|
+
loading: <ServerComponent />,
|
|
2830
|
+
});
|
|
2831
|
+
}, [hydrated]);
|
|
2832
|
+
|
|
2833
|
+
if (!FederatedComponent) {
|
|
2834
|
+
return <ServerComponent />;
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
return (
|
|
2838
|
+
<Suspense fallback={<ServerComponent />}>
|
|
2839
|
+
<FederatedComponent />
|
|
2840
|
+
</Suspense>
|
|
2841
|
+
);
|
|
2842
|
+
};
|
|
2843
|
+
};
|
|
2844
|
+
|
|
2845
|
+
export const Header = createHydratedRemote(HeaderServer, 'explore/Header');
|
|
2846
|
+
export const StorePicker = createHydratedRemote(StorePickerServer, 'explore/StorePicker');
|
|
2847
|
+
export const Recommendations = createHydratedRemote(RecommendationsServer, 'explore/Recommendations');
|
|
2848
|
+
export const ProductPage = createHydratedRemote(ProductPageServer, 'decide/ProductPage');
|
|
2849
|
+
export const MiniCart = createHydratedRemote(MiniCartServer, 'checkout/MiniCart');
|
|
2850
|
+
export const CartPage = createHydratedRemote(CartPageServer, 'checkout/CartPage');
|
|
3286
2851
|
`;
|
|
3287
2852
|
}
|
|
3288
2853
|
function createRemotePage(app) {
|
|
@@ -3327,12 +2892,13 @@ export default function ${toPascalCase(app.id)}Home() {
|
|
|
3327
2892
|
const location = useLocation();
|
|
3328
2893
|
const suffix = locationSuffix(location);
|
|
3329
2894
|
${effectBffState} return (
|
|
3330
|
-
<main>
|
|
2895
|
+
<main className="min-h-screen bg-um-canvas px-4 py-6 text-um-foreground sm:px-8">
|
|
3331
2896
|
<LocalizedHead />
|
|
3332
|
-
<nav aria-label={t('${app.domain}.language.switcher')}>
|
|
2897
|
+
<nav aria-label={t('${app.domain}.language.switcher')} className="flex gap-3">
|
|
3333
2898
|
{supportedLanguages.map(code => (
|
|
3334
2899
|
<a
|
|
3335
2900
|
aria-current={language === code ? 'page' : undefined}
|
|
2901
|
+
className="rounded-full border border-stone-900/15 bg-white px-4 py-2 text-sm font-bold text-stone-950 no-underline"
|
|
3336
2902
|
href={\`\${localizedPath(location.pathname, code)}\${suffix}\`}
|
|
3337
2903
|
key={code}
|
|
3338
2904
|
>
|
|
@@ -3340,9 +2906,9 @@ ${effectBffState} return (
|
|
|
3340
2906
|
</a>
|
|
3341
2907
|
))}
|
|
3342
2908
|
</nav>
|
|
3343
|
-
<h1>{t('${app.domain}.title')}</h1>
|
|
3344
|
-
<p data-mf-role="${app.kind}">{t('${app.domain}.role')}</p>
|
|
3345
|
-
<p data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
|
|
2909
|
+
<h1 className="mt-10 text-5xl font-black">{t('${app.domain}.title')}</h1>
|
|
2910
|
+
<p className="mt-3 text-lg text-stone-600" data-mf-role="${app.kind}">{t('${app.domain}.role')}</p>
|
|
2911
|
+
<p className="sr-only" data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
|
|
3346
2912
|
{ultramodernUiMarker.appId}:{ultramodernUiMarker.version}
|
|
3347
2913
|
</p>
|
|
3348
2914
|
${effectBffMarkup} </main>
|
|
@@ -3370,9 +2936,9 @@ function createRemoteEntry(app) {
|
|
|
3370
2936
|
`;
|
|
3371
2937
|
return `export default function ${toPascalCase(app.domain ?? app.id)}Route() {
|
|
3372
2938
|
return (
|
|
3373
|
-
<section data-mf-remote="${app.id}" data-mf-expose="./Route">
|
|
3374
|
-
<h2>${app.displayName}</h2>
|
|
3375
|
-
<p>Route surface for ${app.domain ?? app.id}.</p>
|
|
2939
|
+
<section className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10" data-mf-remote="${app.id}" data-mf-expose="./Route">
|
|
2940
|
+
<h2 className="text-2xl font-black">${app.displayName}</h2>
|
|
2941
|
+
<p className="mt-2 text-stone-600">Route surface for ${app.domain ?? app.id}.</p>
|
|
3376
2942
|
</section>
|
|
3377
2943
|
);
|
|
3378
2944
|
}
|
|
@@ -3383,9 +2949,9 @@ function createRemoteWidget(app) {
|
|
|
3383
2949
|
const body = 'vertical' === app.kind ? `Owns the ${app.domain} vertical route surface.` : 'Provides shared UI primitives for the workspace.';
|
|
3384
2950
|
return `export default function ${componentName}() {
|
|
3385
2951
|
return (
|
|
3386
|
-
<section data-mf-remote="${app.id}">
|
|
3387
|
-
<h2>${app.displayName}</h2>
|
|
3388
|
-
<p>${body}</p>
|
|
2952
|
+
<section className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10" data-mf-remote="${app.id}">
|
|
2953
|
+
<h2 className="text-2xl font-black">${app.displayName}</h2>
|
|
2954
|
+
<p className="mt-2 text-stone-600">${body}</p>
|
|
3389
2955
|
</section>
|
|
3390
2956
|
);
|
|
3391
2957
|
}
|
|
@@ -3399,23 +2965,27 @@ export default function Header() {
|
|
|
3399
2965
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3400
2966
|
|
|
3401
2967
|
return (
|
|
3402
|
-
<header className="
|
|
3403
|
-
<a className="
|
|
3404
|
-
<nav aria-label={t('explore.header.navigation')} className="
|
|
3405
|
-
<a href={\`/\${language}/tractors\`}>{t('explore.header.machines')}</a>
|
|
3406
|
-
<a href={\`/\${language}/stores\`}>{t('explore.header.stores')}</a>
|
|
2968
|
+
<header className="flex min-w-0 flex-1 flex-wrap items-center gap-x-8 gap-y-2" data-mf-boundary="explore">
|
|
2969
|
+
<a className="whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline" href={\`/\${language}\`}>Acre & Iron</a>
|
|
2970
|
+
<nav aria-label={t('explore.header.navigation')} className="flex items-center gap-5">
|
|
2971
|
+
<a className="text-sm font-extrabold text-stone-900 no-underline" href={\`/\${language}/tractors\`}>{t('explore.header.machines')}</a>
|
|
2972
|
+
<a className="text-sm font-extrabold text-stone-900 no-underline" href={\`/\${language}/stores\`}>{t('explore.header.stores')}</a>
|
|
3407
2973
|
</nav>
|
|
3408
2974
|
</header>
|
|
3409
2975
|
);
|
|
3410
2976
|
}
|
|
3411
2977
|
`;
|
|
3412
2978
|
if ('remote-explore' === app.id && './Recommendations' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2979
|
+
import autonomyImage from '../assets/autonomy.svg';
|
|
2980
|
+
import fieldLoaderImage from '../assets/field-loader.svg';
|
|
2981
|
+
import orchardImage from '../assets/orchard.svg';
|
|
2982
|
+
import vineyardImage from '../assets/vineyard.svg';
|
|
3413
2983
|
|
|
3414
2984
|
const tractors = [
|
|
3415
|
-
{ badge: 'explore.recommendations.bestRows', name: 'Orchard Tractor', slug: 'orchard-tractor' },
|
|
3416
|
-
{ badge: 'explore.recommendations.aiFirst', name: 'Autonomy Retrofit Kit', slug: 'autonomy-retrofit-kit' },
|
|
3417
|
-
{ badge: 'explore.recommendations.loaderReady', name: 'Field Loader 112', slug: 'field-loader-112' },
|
|
3418
|
-
{ badge: 'explore.recommendations.vineyard', name: 'Vineyard Narrow 80', slug: 'vineyard-narrow-80' },
|
|
2985
|
+
{ badge: 'explore.recommendations.bestRows', image: orchardImage, name: 'Orchard Tractor', slug: 'orchard-tractor' },
|
|
2986
|
+
{ badge: 'explore.recommendations.aiFirst', image: autonomyImage, name: 'Autonomy Retrofit Kit', slug: 'autonomy-retrofit-kit' },
|
|
2987
|
+
{ badge: 'explore.recommendations.loaderReady', image: fieldLoaderImage, name: 'Field Loader 112', slug: 'field-loader-112' },
|
|
2988
|
+
{ badge: 'explore.recommendations.vineyard', image: vineyardImage, name: 'Vineyard Narrow 80', slug: 'vineyard-narrow-80' },
|
|
3419
2989
|
] as const;
|
|
3420
2990
|
|
|
3421
2991
|
export default function Recommendations() {
|
|
@@ -3423,13 +2993,14 @@ export default function Recommendations() {
|
|
|
3423
2993
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3424
2994
|
|
|
3425
2995
|
return (
|
|
3426
|
-
<section className="
|
|
3427
|
-
<h2 className="
|
|
3428
|
-
<div className="
|
|
2996
|
+
<section className="mx-auto mt-12 max-w-7xl" data-mf-boundary="explore">
|
|
2997
|
+
<h2 className="text-3xl font-black tracking-normal text-stone-950">{t('explore.recommendations.title')}</h2>
|
|
2998
|
+
<div className="mt-5 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
3429
2999
|
{tractors.map(tractor => (
|
|
3430
|
-
<a className="
|
|
3431
|
-
<
|
|
3432
|
-
<
|
|
3000
|
+
<a className="block rounded-2xl bg-white/90 p-4 text-stone-950 no-underline shadow-xl shadow-stone-900/10 transition hover:-translate-y-0.5 hover:shadow-2xl" href={\`/\${language}/tractors/\${tractor.slug}\`} key={tractor.slug}>
|
|
3001
|
+
<img alt="" className="aspect-video w-full rounded-xl bg-stone-200 object-cover" src={tractor.image} />
|
|
3002
|
+
<span className="mt-4 block text-xs font-black uppercase tracking-[0.16em] text-amber-700">{t(tractor.badge)}</span>
|
|
3003
|
+
<strong className="mt-2 block text-xl font-black leading-tight">{tractor.name}</strong>
|
|
3433
3004
|
</a>
|
|
3434
3005
|
))}
|
|
3435
3006
|
</div>
|
|
@@ -3438,22 +3009,26 @@ export default function Recommendations() {
|
|
|
3438
3009
|
}
|
|
3439
3010
|
`;
|
|
3440
3011
|
if ('remote-explore' === app.id && './StorePicker' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
3012
|
+
import fieldLoaderImage from '../assets/field-loader.svg';
|
|
3013
|
+
import vineyardImage from '../assets/vineyard.svg';
|
|
3441
3014
|
|
|
3442
3015
|
export default function StorePicker() {
|
|
3443
3016
|
const { i18nInstance } = useModernI18n();
|
|
3444
3017
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3445
3018
|
|
|
3446
3019
|
return (
|
|
3447
|
-
<section className="
|
|
3448
|
-
<h2 className="
|
|
3449
|
-
<div className="
|
|
3450
|
-
<article className="
|
|
3451
|
-
<
|
|
3452
|
-
<
|
|
3020
|
+
<section className="mx-auto mt-12 max-w-7xl" data-mf-boundary="explore">
|
|
3021
|
+
<h2 className="text-3xl font-black tracking-normal text-stone-950">{t('explore.stores.title')}</h2>
|
|
3022
|
+
<div className="mt-5 grid gap-4 md:grid-cols-2">
|
|
3023
|
+
<article className="rounded-2xl bg-white/90 p-4 shadow-xl shadow-stone-900/10">
|
|
3024
|
+
<img alt="" className="aspect-video w-full rounded-xl bg-stone-200 object-cover" src={fieldLoaderImage} />
|
|
3025
|
+
<span className="mt-4 block text-xs font-black uppercase tracking-[0.16em] text-emerald-800">{t('explore.stores.northRegion')}</span>
|
|
3026
|
+
<strong className="mt-2 block text-2xl font-black">Bohemia Field Supply</strong>
|
|
3453
3027
|
</article>
|
|
3454
|
-
<article className="
|
|
3455
|
-
<
|
|
3456
|
-
<
|
|
3028
|
+
<article className="rounded-2xl bg-white/90 p-4 shadow-xl shadow-stone-900/10">
|
|
3029
|
+
<img alt="" className="aspect-video w-full rounded-xl bg-stone-200 object-cover" src={vineyardImage} />
|
|
3030
|
+
<span className="mt-4 block text-xs font-black uppercase tracking-[0.16em] text-emerald-800">{t('explore.stores.southRegion')}</span>
|
|
3031
|
+
<strong className="mt-2 block text-2xl font-black">Moravia Iron Works</strong>
|
|
3457
3032
|
</article>
|
|
3458
3033
|
</div>
|
|
3459
3034
|
</section>
|
|
@@ -3461,12 +3036,13 @@ export default function StorePicker() {
|
|
|
3461
3036
|
}
|
|
3462
3037
|
`;
|
|
3463
3038
|
if ('remote-explore' === app.id && './Footer' === expose) return `export default function Footer() {
|
|
3464
|
-
return <footer className="
|
|
3039
|
+
return <footer className="mx-auto mt-12 max-w-7xl text-sm font-bold text-stone-600" data-mf-boundary="explore">Acre & Iron</footer>;
|
|
3465
3040
|
}
|
|
3466
3041
|
`;
|
|
3467
3042
|
if ('./Widget' === expose) return createRemoteWidget(app);
|
|
3468
3043
|
const componentName = `${toPascalCase(app.domain ?? app.id)}${toPascalCase(expose.replace(/^\.\//u, ''))}`;
|
|
3469
3044
|
if ('remote-decide' === app.id && './ProductPage' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
3045
|
+
import fieldLoaderImage from '../assets/field-loader.svg';
|
|
3470
3046
|
import { AddToCart, Recommendations } from './remote-components';
|
|
3471
3047
|
|
|
3472
3048
|
export default function ${componentName}() {
|
|
@@ -3475,16 +3051,16 @@ export default function ${componentName}() {
|
|
|
3475
3051
|
|
|
3476
3052
|
return (
|
|
3477
3053
|
<>
|
|
3478
|
-
<section className="
|
|
3479
|
-
<
|
|
3054
|
+
<section className="mx-auto mt-10 grid max-w-7xl items-center gap-8 md:grid-cols-[1fr_0.95fr] lg:gap-14" data-mf-boundary="decide" data-mf-remote="${app.id}" data-mf-expose="${expose}">
|
|
3055
|
+
<img alt="" className="aspect-[1/0.9] w-full rounded-3xl border-[18px] border-amber-200 bg-stone-200 object-cover shadow-2xl shadow-stone-900/20" src={fieldLoaderImage} />
|
|
3480
3056
|
<div>
|
|
3481
|
-
<p className="
|
|
3482
|
-
<h1 className="
|
|
3483
|
-
<p className="
|
|
3484
|
-
<div className="
|
|
3485
|
-
<article className="
|
|
3486
|
-
<article className="
|
|
3487
|
-
<article className="
|
|
3057
|
+
<p className="text-xs font-black uppercase tracking-[0.18em] text-emerald-800">{t('decide.product.eyebrow')}</p>
|
|
3058
|
+
<h1 className="mt-3 text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl">Field Loader 112</h1>
|
|
3059
|
+
<p className="mt-5 max-w-2xl text-lg leading-8 text-stone-600">{t('decide.product.lede')}</p>
|
|
3060
|
+
<div className="mt-8 grid gap-4 sm:grid-cols-3">
|
|
3061
|
+
<article className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10"><span className="block text-sm font-bold text-stone-500">{t('decide.product.price')}</span><strong className="mt-2 block text-lg font-black">EUR 42,500</strong></article>
|
|
3062
|
+
<article className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10"><span className="block text-sm font-bold text-stone-500">{t('decide.product.power')}</span><strong className="mt-2 block text-lg font-black">112 hp</strong></article>
|
|
3063
|
+
<article className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10"><span className="block text-sm font-bold text-stone-500">{t('decide.product.availability')}</span><strong className="mt-2 block text-lg font-black">{t('decide.product.inStock')}</strong></article>
|
|
3488
3064
|
</div>
|
|
3489
3065
|
<AddToCart />
|
|
3490
3066
|
</div>
|
|
@@ -3503,11 +3079,11 @@ export default function ${componentName}() {
|
|
|
3503
3079
|
const cart = useCartLines();
|
|
3504
3080
|
|
|
3505
3081
|
return (
|
|
3506
|
-
<div className="
|
|
3507
|
-
<button className="
|
|
3082
|
+
<div className="mt-8 flex flex-wrap gap-3" data-mf-boundary="checkout">
|
|
3083
|
+
<button className="inline-flex min-h-11 items-center justify-center rounded-full bg-emerald-800 px-5 font-bold text-white shadow-lg shadow-stone-900/10" onClick={cart.addFieldLoader} type="button">
|
|
3508
3084
|
{t('checkout.actions.addToCart')}
|
|
3509
3085
|
</button>
|
|
3510
|
-
<a className="
|
|
3086
|
+
<a className="inline-flex min-h-11 items-center justify-center rounded-full border border-stone-900/15 bg-white/90 px-5 font-bold text-stone-950 shadow-lg shadow-stone-900/10" href={\`/\${language}/cart\`}>
|
|
3511
3087
|
{t('checkout.actions.viewCart')}
|
|
3512
3088
|
</a>
|
|
3513
3089
|
</div>
|
|
@@ -3524,7 +3100,7 @@ export default function ${componentName}() {
|
|
|
3524
3100
|
const count = cart.lines.reduce((sum, line) => sum + line.quantity, 0);
|
|
3525
3101
|
|
|
3526
3102
|
return (
|
|
3527
|
-
<a className="
|
|
3103
|
+
<a className="inline-flex h-10 shrink-0 items-center justify-center rounded-full border border-stone-900/15 bg-white px-4 text-sm font-extrabold text-stone-950 no-underline shadow-lg shadow-stone-900/5" data-mf-boundary="checkout" href={\`/\${language}/cart\`}>
|
|
3528
3104
|
{t('checkout.cart.title')} ({count})
|
|
3529
3105
|
</a>
|
|
3530
3106
|
);
|
|
@@ -3539,24 +3115,24 @@ export default function ${componentName}() {
|
|
|
3539
3115
|
const cart = useCartLines();
|
|
3540
3116
|
|
|
3541
3117
|
return (
|
|
3542
|
-
<section className="
|
|
3543
|
-
<h1 className="
|
|
3544
|
-
<div className="
|
|
3118
|
+
<section className="mx-auto mt-10 max-w-7xl" data-mf-boundary="checkout" data-mf-remote="${app.id}" data-mf-expose="${expose}">
|
|
3119
|
+
<h1 className="text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl">{t('checkout.cart.title')}</h1>
|
|
3120
|
+
<div className="mt-8 rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10">
|
|
3545
3121
|
{cart.lines.length === 0 ? (
|
|
3546
3122
|
<p>{t('checkout.cart.empty')}</p>
|
|
3547
3123
|
) : (
|
|
3548
3124
|
<>
|
|
3549
3125
|
{cart.lines.map(line => (
|
|
3550
|
-
<article className="
|
|
3126
|
+
<article className="grid gap-4 border-t border-stone-900/10 py-4 first:border-t-0 sm:grid-cols-[1fr_auto] sm:items-center" key={line.id}>
|
|
3551
3127
|
<div>
|
|
3552
|
-
<strong>{line.name}</strong>
|
|
3553
|
-
<p>EUR {line.price.toLocaleString('en-US')}</p>
|
|
3128
|
+
<strong className="text-lg font-black">{line.name}</strong>
|
|
3129
|
+
<p className="text-stone-600">EUR {line.price.toLocaleString('en-US')}</p>
|
|
3554
3130
|
</div>
|
|
3555
|
-
<div className="
|
|
3556
|
-
<button className="
|
|
3557
|
-
<span>{line.quantity}</span>
|
|
3558
|
-
<button className="
|
|
3559
|
-
<button className="
|
|
3131
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
3132
|
+
<button className="inline-flex size-9 items-center justify-center rounded-full border border-stone-900/15 bg-white font-black" onClick={() => cart.decrement(line.id)} type="button">-</button>
|
|
3133
|
+
<span className="min-w-6 text-center font-black">{line.quantity}</span>
|
|
3134
|
+
<button className="inline-flex size-9 items-center justify-center rounded-full border border-stone-900/15 bg-white font-black" onClick={() => cart.increment(line.id)} type="button">+</button>
|
|
3135
|
+
<button className="inline-flex min-h-10 items-center justify-center rounded-full border border-stone-900/15 bg-white px-4 font-bold text-stone-950" onClick={() => cart.remove(line.id)} type="button">
|
|
3560
3136
|
{t('checkout.actions.remove')}
|
|
3561
3137
|
</button>
|
|
3562
3138
|
</div>
|
|
@@ -3572,21 +3148,23 @@ export default function ${componentName}() {
|
|
|
3572
3148
|
`;
|
|
3573
3149
|
return `export default function ${componentName}() {
|
|
3574
3150
|
return (
|
|
3575
|
-
<section data-mf-remote="${app.id}" data-mf-expose="${expose}">
|
|
3576
|
-
<h2>${app.displayName} ${expose.replace(/^\.\//u, '')}</h2>
|
|
3577
|
-
<p>Module Federation surface owned by ${app.ownership.team}.</p>
|
|
3151
|
+
<section className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10" data-mf-remote="${app.id}" data-mf-expose="${expose}">
|
|
3152
|
+
<h2 className="text-2xl font-black">${app.displayName} ${expose.replace(/^\.\//u, '')}</h2>
|
|
3153
|
+
<p className="mt-2 text-stone-600">Module Federation surface owned by ${app.ownership.team}.</p>
|
|
3578
3154
|
</section>
|
|
3579
3155
|
);
|
|
3580
3156
|
}
|
|
3581
3157
|
`;
|
|
3582
3158
|
}
|
|
3583
|
-
function createDecideRemoteComponents() {
|
|
3159
|
+
function createDecideRemoteComponents(scope) {
|
|
3584
3160
|
return `import { createLazyComponent } from '@module-federation/modern-js-v3/react';
|
|
3585
3161
|
import { getInstance, loadRemote } from '@module-federation/modern-js-v3/runtime';
|
|
3586
|
-
import type
|
|
3162
|
+
import { Suspense, useEffect, useMemo, useState, type ComponentType } from 'react';
|
|
3163
|
+
import RecommendationsServer from '${ultramodern_workspace_packageName(scope, 'remote-explore')}/Recommendations';
|
|
3164
|
+
import AddToCartServer from '${ultramodern_workspace_packageName(scope, 'remote-checkout')}/AddToCart';
|
|
3587
3165
|
|
|
3588
3166
|
type RemoteComponentModule = {
|
|
3589
|
-
default:
|
|
3167
|
+
default: ComponentType;
|
|
3590
3168
|
};
|
|
3591
3169
|
|
|
3592
3170
|
const loadRemoteComponent = async (specifier: string) => {
|
|
@@ -3599,24 +3177,50 @@ const loadRemoteComponent = async (specifier: string) => {
|
|
|
3599
3177
|
|
|
3600
3178
|
const remoteFallback =
|
|
3601
3179
|
({ error }: { error: Error }) =>
|
|
3602
|
-
<div data-remote-error={error.name}>Remote unavailable</div>;
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3180
|
+
<div className="rounded-xl border border-red-900/20 bg-red-50 px-4 py-3 text-sm font-semibold text-red-900" data-remote-error={error.name}>Remote unavailable</div>;
|
|
3181
|
+
|
|
3182
|
+
const createHydratedRemote = (
|
|
3183
|
+
ServerComponent: ComponentType,
|
|
3184
|
+
specifier: string,
|
|
3185
|
+
) => {
|
|
3186
|
+
return function HydratedRemote() {
|
|
3187
|
+
const [hydrated, setHydrated] = useState(false);
|
|
3188
|
+
|
|
3189
|
+
useEffect(() => {
|
|
3190
|
+
setHydrated(true);
|
|
3191
|
+
}, []);
|
|
3192
|
+
|
|
3193
|
+
const FederatedComponent = useMemo(() => {
|
|
3194
|
+
if (!hydrated) {
|
|
3195
|
+
return undefined;
|
|
3196
|
+
}
|
|
3197
|
+
const instance = getInstance();
|
|
3198
|
+
if (!instance) {
|
|
3199
|
+
return undefined;
|
|
3200
|
+
}
|
|
3201
|
+
return createLazyComponent({
|
|
3202
|
+
export: 'default',
|
|
3203
|
+
fallback: remoteFallback,
|
|
3204
|
+
instance,
|
|
3205
|
+
loader: () => loadRemoteComponent(specifier),
|
|
3206
|
+
loading: <ServerComponent />,
|
|
3207
|
+
});
|
|
3208
|
+
}, [hydrated]);
|
|
3209
|
+
|
|
3210
|
+
if (!FederatedComponent) {
|
|
3211
|
+
return <ServerComponent />;
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
return (
|
|
3215
|
+
<Suspense fallback={<ServerComponent />}>
|
|
3216
|
+
<FederatedComponent />
|
|
3217
|
+
</Suspense>
|
|
3218
|
+
);
|
|
3219
|
+
};
|
|
3220
|
+
};
|
|
3221
|
+
|
|
3222
|
+
export const AddToCart = createHydratedRemote(AddToCartServer, 'checkout/AddToCart');
|
|
3223
|
+
export const Recommendations = createHydratedRemote(RecommendationsServer, 'explore/Recommendations');
|
|
3620
3224
|
`;
|
|
3621
3225
|
}
|
|
3622
3226
|
function remoteComponentOutputPath(app, expose) {
|
|
@@ -3749,17 +3353,9 @@ function createAppLocaleMessages(app, language) {
|
|
|
3749
3353
|
};
|
|
3750
3354
|
}
|
|
3751
3355
|
function createDesignButton() {
|
|
3752
|
-
return `
|
|
3753
|
-
|
|
3754
|
-
export default function Button({ label }: { label: string }) {
|
|
3356
|
+
return `export default function Button({ label }: { label: string }) {
|
|
3755
3357
|
return (
|
|
3756
|
-
<button
|
|
3757
|
-
type="button"
|
|
3758
|
-
style={{
|
|
3759
|
-
borderRadius: designTokens.radius.control,
|
|
3760
|
-
color: designTokens.color.foreground,
|
|
3761
|
-
}}
|
|
3762
|
-
>
|
|
3358
|
+
<button className="rounded-full text-um-foreground" type="button">
|
|
3763
3359
|
{label}
|
|
3764
3360
|
</button>
|
|
3765
3361
|
);
|
|
@@ -3875,14 +3471,12 @@ export function useCartLines() {
|
|
|
3875
3471
|
`;
|
|
3876
3472
|
}
|
|
3877
3473
|
function createSharedDesignTokensCss() {
|
|
3878
|
-
return `@
|
|
3879
|
-
:
|
|
3880
|
-
--um-
|
|
3881
|
-
--um-
|
|
3882
|
-
--um-
|
|
3883
|
-
--um-
|
|
3884
|
-
--um-color-surface: #f6fbf7;
|
|
3885
|
-
}
|
|
3474
|
+
return `@theme {
|
|
3475
|
+
--color-um-accent: #2f8f68;
|
|
3476
|
+
--color-um-canvas: #f1eadc;
|
|
3477
|
+
--color-um-foreground: #133225;
|
|
3478
|
+
--color-um-link: #166b4b;
|
|
3479
|
+
--color-um-surface: #f6fbf7;
|
|
3886
3480
|
}
|
|
3887
3481
|
`;
|
|
3888
3482
|
}
|
|
@@ -5788,10 +5382,12 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind) {
|
|
|
5788
5382
|
}
|
|
5789
5383
|
writeFile(targetDir, `${app.directory}/module-federation.config.ts`, 'shell' === app.kind ? createShellModuleFederationConfig() : createRemoteModuleFederationConfig(app));
|
|
5790
5384
|
writeFile(targetDir, `${app.directory}/src/routes/layout.tsx`, createLayout(app.id));
|
|
5385
|
+
for (const [relativePath, content] of Object.entries(commerceAssetsForApp(app)))writeFile(targetDir, `${app.directory}/${relativePath}`, content);
|
|
5791
5386
|
writeFile(targetDir, `${app.directory}/src/routes/[lang]/page.tsx`, 'shell' === app.kind ? createShellPage() : createRemotePage(app));
|
|
5792
5387
|
for (const route of createRouteOwnedI18nPaths(app))if ('/' !== route.canonicalPath && 'shell' !== app.kind) writeFile(targetDir, createRoutePageFilePath(app, route.canonicalPath), createRouteAliasPage(route.canonicalPath));
|
|
5793
5388
|
if ('shell' === app.kind) {
|
|
5794
|
-
writeFile(targetDir, `${app.directory}/src/routes/remote-components.tsx`, createShellRemoteComponents());
|
|
5389
|
+
writeFile(targetDir, `${app.directory}/src/routes/remote-components.tsx`, createShellRemoteComponents(scope));
|
|
5390
|
+
writeFile(targetDir, `${app.directory}/src/routes/shell-frame.tsx`, createShellFrameComponent());
|
|
5795
5391
|
writeFile(targetDir, `${app.directory}/src/routes/boundary-overlay.tsx`, createShellBoundaryOverlay());
|
|
5796
5392
|
writeFile(targetDir, `${app.directory}/src/effect/recommendations-client.ts`, createShellEffectClient(scope));
|
|
5797
5393
|
writeFile(targetDir, `${app.directory}/src/routes/[lang]/tractors/page.tsx`, createShellTractorsPage());
|
|
@@ -5805,7 +5401,7 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind) {
|
|
|
5805
5401
|
}
|
|
5806
5402
|
if ('vertical' === app.kind || 'horizontal-remote' === app.kind) {
|
|
5807
5403
|
writeFile(targetDir, `${app.directory}/src/remote-entry.tsx`, createRemoteEntry(app));
|
|
5808
|
-
if ('remote-decide' === app.id) writeFile(targetDir, `${app.directory}/src/components/remote-components.tsx`, createDecideRemoteComponents());
|
|
5404
|
+
if ('remote-decide' === app.id) writeFile(targetDir, `${app.directory}/src/components/remote-components.tsx`, createDecideRemoteComponents(scope));
|
|
5809
5405
|
if ('remote-checkout' === app.id) writeFile(targetDir, `${app.directory}/src/cart-store.ts`, createCheckoutCartStore());
|
|
5810
5406
|
for (const expose of Object.keys(app.exposes ?? {})){
|
|
5811
5407
|
const outputPath = remoteComponentOutputPath(app, expose);
|
|
@@ -5824,7 +5420,13 @@ function writeEffectService(targetDir, scope, packageSource, enableTailwind, ser
|
|
|
5824
5420
|
writeFile(targetDir, `${service.directory}/src/ultramodern-build.ts`, createUltramodernBuildModule(scope, service));
|
|
5825
5421
|
writeFile(targetDir, `${service.directory}/src/routes/layout.tsx`, createLayout(service.id));
|
|
5826
5422
|
writeFile(targetDir, `${service.directory}/src/routes/page.tsx`, `export default function ${toPascalCase(service.id)}Home() {
|
|
5827
|
-
return
|
|
5423
|
+
return (
|
|
5424
|
+
<main className="min-h-screen bg-um-canvas px-4 py-6 text-um-foreground sm:px-8">
|
|
5425
|
+
<section className="rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10">
|
|
5426
|
+
<h1 className="text-3xl font-black">${service.id} Effect service</h1>
|
|
5427
|
+
</section>
|
|
5428
|
+
</main>
|
|
5429
|
+
);
|
|
5828
5430
|
}
|
|
5829
5431
|
`);
|
|
5830
5432
|
writeFile(targetDir, `${service.directory}/src/routes/index.css`, createServiceStyles(enableTailwind, scope, service));
|