@bleedingdev/modern-js-create 3.2.0-ultramodern.29 → 3.2.0-ultramodern.30
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 +820 -33
- package/package.json +4 -4
- package/template/.github/workflows/ultramodern-gates.yml.handlebars +8 -9
- package/template/.mise.toml.handlebars +2 -0
- package/template/AGENTS.md +1 -1
- package/template/README.md +16 -15
- package/template/package.json.handlebars +3 -3
- package/template/scripts/validate-ultramodern.mjs.handlebars +29 -53
- package/template/src/routes/[lang]/page.tsx.handlebars +1 -2
- package/template/src/routes/layout.tsx.handlebars +1 -0
- package/template/tests/ultramodern.contract.test.ts.handlebars +3 -20
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +8 -9
- package/template-workspace/.mise.toml.handlebars +2 -0
- package/template-workspace/AGENTS.md +2 -2
- package/template-workspace/README.md.handlebars +6 -4
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +38 -44
- package/template/.mise.toml +0 -2
- package/template-workspace/.mise.toml +0 -2
package/dist/index.js
CHANGED
|
@@ -453,8 +453,8 @@ const EN_LOCALE = {
|
|
|
453
453
|
success: '✨ Created successfully!',
|
|
454
454
|
nextSteps: '📋 Next steps:',
|
|
455
455
|
step1: 'cd {projectName}',
|
|
456
|
-
step2: 'pnpm install',
|
|
457
|
-
step3: 'pnpm dev'
|
|
456
|
+
step2: 'mise exec -- pnpm install',
|
|
457
|
+
step3: 'mise exec -- pnpm dev'
|
|
458
458
|
},
|
|
459
459
|
help: {
|
|
460
460
|
title: '🚀 Modern.js Project Creator',
|
|
@@ -510,8 +510,8 @@ const ZH_LOCALE = {
|
|
|
510
510
|
success: '✨ 创建成功!',
|
|
511
511
|
nextSteps: '📋 下一步:',
|
|
512
512
|
step1: 'cd {projectName}',
|
|
513
|
-
step2: 'pnpm install',
|
|
514
|
-
step3: 'pnpm dev'
|
|
513
|
+
step2: 'mise exec -- pnpm install',
|
|
514
|
+
step3: 'mise exec -- pnpm dev'
|
|
515
515
|
},
|
|
516
516
|
help: {
|
|
517
517
|
title: '🚀 Modern.js 项目创建工具',
|
|
@@ -567,13 +567,14 @@ const TAILWIND_VERSION = '4.3.0';
|
|
|
567
567
|
const TAILWIND_POSTCSS_VERSION = '4.3.0';
|
|
568
568
|
const EFFECT_TSGO_VERSION = '0.11.0';
|
|
569
569
|
const TYPESCRIPT_VERSION = '6.0.3';
|
|
570
|
-
const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.
|
|
570
|
+
const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260527.2';
|
|
571
571
|
const OXLINT_VERSION = '1.66.0';
|
|
572
572
|
const OXFMT_VERSION = '0.51.0';
|
|
573
573
|
const ULTRACITE_VERSION = '7.7.0';
|
|
574
574
|
const I18NEXT_VERSION = '26.2.0';
|
|
575
575
|
const REACT_VERSION = '^19.2.6';
|
|
576
576
|
const REACT_DOM_VERSION = '^19.2.6';
|
|
577
|
+
const PNPM_VERSION = '11.4.0';
|
|
577
578
|
const WORKSPACE_PACKAGE_VERSION = 'workspace:*';
|
|
578
579
|
const GENERATED_CONTRACT_PATH = '.modernjs/ultramodern-generated-contract.json';
|
|
579
580
|
const RSTACK_AGENT_SKILLS_COMMIT = '61c948b42512e223bad44b83af4080eba48b2677';
|
|
@@ -1097,7 +1098,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
1097
1098
|
name: scope,
|
|
1098
1099
|
version: '0.1.0',
|
|
1099
1100
|
type: 'module',
|
|
1100
|
-
packageManager:
|
|
1101
|
+
packageManager: `pnpm@${PNPM_VERSION}`,
|
|
1101
1102
|
scripts: {
|
|
1102
1103
|
dev: `pnpm --parallel --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} --filter ${ultramodern_workspace_packageName(scope, 'remote-commerce')} --filter ${ultramodern_workspace_packageName(scope, 'remote-identity')} --filter ${ultramodern_workspace_packageName(scope, 'remote-design-system')} dev`,
|
|
1103
1104
|
'dev:shell': `pnpm --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} dev`,
|
|
@@ -1122,7 +1123,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
1122
1123
|
},
|
|
1123
1124
|
engines: {
|
|
1124
1125
|
node: '>=20',
|
|
1125
|
-
pnpm:
|
|
1126
|
+
pnpm: `>=${PNPM_VERSION} <11.5.0`
|
|
1126
1127
|
},
|
|
1127
1128
|
workspaces: [
|
|
1128
1129
|
'apps/*',
|
|
@@ -1674,8 +1675,9 @@ export default defineRuntimeConfig({
|
|
|
1674
1675
|
function createAppStyles(enableTailwind) {
|
|
1675
1676
|
return `${enableTailwind ? "@import 'tailwindcss';\n\n" : ''}:root {
|
|
1676
1677
|
color: #10231c;
|
|
1677
|
-
background: #
|
|
1678
|
+
background: #f1eadc;
|
|
1678
1679
|
font-family:
|
|
1680
|
+
Geist,
|
|
1679
1681
|
Inter,
|
|
1680
1682
|
ui-sans-serif,
|
|
1681
1683
|
system-ui,
|
|
@@ -1703,6 +1705,288 @@ nav {
|
|
|
1703
1705
|
a {
|
|
1704
1706
|
color: #166b4b;
|
|
1705
1707
|
}
|
|
1708
|
+
|
|
1709
|
+
.commerce-shell {
|
|
1710
|
+
background: #f1eadc;
|
|
1711
|
+
color: #0b0a08;
|
|
1712
|
+
min-height: 100vh;
|
|
1713
|
+
padding: 1.5rem clamp(1rem, 4vw, 3rem) 4rem;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
.commerce-header,
|
|
1717
|
+
.commerce-footer {
|
|
1718
|
+
align-items: center;
|
|
1719
|
+
background: rgba(255, 255, 255, 0.86);
|
|
1720
|
+
box-shadow: 0 0.625rem 1.875rem rgba(25, 20, 12, 0.08);
|
|
1721
|
+
display: flex;
|
|
1722
|
+
gap: 1.25rem;
|
|
1723
|
+
justify-content: space-between;
|
|
1724
|
+
margin: 0 auto;
|
|
1725
|
+
max-width: 88rem;
|
|
1726
|
+
padding: 1.25rem 1.75rem;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
.commerce-logo {
|
|
1730
|
+
font-size: 1.35rem;
|
|
1731
|
+
font-weight: 800;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
.commerce-nav,
|
|
1735
|
+
.commerce-actions,
|
|
1736
|
+
.commerce-language {
|
|
1737
|
+
align-items: center;
|
|
1738
|
+
display: flex;
|
|
1739
|
+
flex-wrap: wrap;
|
|
1740
|
+
gap: 0.75rem;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
.commerce-nav {
|
|
1744
|
+
margin: 0;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
.commerce-pill,
|
|
1748
|
+
.commerce-button,
|
|
1749
|
+
.commerce-link-button,
|
|
1750
|
+
.commerce-cart-button,
|
|
1751
|
+
.commerce-quantity-button {
|
|
1752
|
+
align-items: center;
|
|
1753
|
+
border-radius: 999px;
|
|
1754
|
+
border: 0.0625rem solid rgba(23, 23, 23, 0.14);
|
|
1755
|
+
box-shadow: 0 0.25rem 0.75rem rgba(20, 17, 10, 0.08);
|
|
1756
|
+
color: #14120d;
|
|
1757
|
+
display: inline-flex;
|
|
1758
|
+
font: inherit;
|
|
1759
|
+
font-weight: 750;
|
|
1760
|
+
justify-content: center;
|
|
1761
|
+
min-height: 2.5rem;
|
|
1762
|
+
padding: 0.65rem 1.05rem;
|
|
1763
|
+
text-decoration: none;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
.commerce-button {
|
|
1767
|
+
background: #00624b;
|
|
1768
|
+
border-color: #00624b;
|
|
1769
|
+
color: #ffffff;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
.commerce-link-button,
|
|
1773
|
+
.commerce-pill,
|
|
1774
|
+
.commerce-cart-button,
|
|
1775
|
+
.commerce-quantity-button {
|
|
1776
|
+
background: rgba(255, 255, 255, 0.92);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
.commerce-page {
|
|
1780
|
+
margin: 3rem auto 0;
|
|
1781
|
+
max-width: 88rem;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
.commerce-product {
|
|
1785
|
+
align-items: center;
|
|
1786
|
+
display: grid;
|
|
1787
|
+
gap: clamp(2rem, 5vw, 4rem);
|
|
1788
|
+
grid-template-columns: minmax(0, 1fr) minmax(20rem, 0.95fr);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
.commerce-product-media {
|
|
1792
|
+
aspect-ratio: 1 / 0.92;
|
|
1793
|
+
background:
|
|
1794
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0) 42%),
|
|
1795
|
+
linear-gradient(135deg, #97c66d 0 20%, #6f9748 20% 34%, #d6c15d 34% 36%, #6a8f3e 36% 46%, #315824 46% 64%, #8bb85e 64% 100%);
|
|
1796
|
+
border: 1.25rem solid #ffe987;
|
|
1797
|
+
border-radius: 1.6rem;
|
|
1798
|
+
box-shadow: inset 0 -7rem 8rem rgba(58, 77, 35, 0.22);
|
|
1799
|
+
overflow: hidden;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
.commerce-product-media::after {
|
|
1803
|
+
background:
|
|
1804
|
+
radial-gradient(circle at 27% 76%, #1e2422 0 5%, transparent 5.4%),
|
|
1805
|
+
radial-gradient(circle at 55% 76%, #1e2422 0 6%, transparent 6.4%),
|
|
1806
|
+
linear-gradient(0deg, #004b7b 0 100%);
|
|
1807
|
+
border-radius: 1.2rem;
|
|
1808
|
+
content: "";
|
|
1809
|
+
display: block;
|
|
1810
|
+
height: 19%;
|
|
1811
|
+
margin: 58% auto 0;
|
|
1812
|
+
width: 42%;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
.commerce-eyebrow {
|
|
1816
|
+
color: #00624b;
|
|
1817
|
+
font-size: 0.85rem;
|
|
1818
|
+
font-weight: 850;
|
|
1819
|
+
letter-spacing: 0.16rem;
|
|
1820
|
+
text-transform: uppercase;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
.commerce-title {
|
|
1824
|
+
font-size: clamp(2.5rem, 6vw, 4.8rem);
|
|
1825
|
+
line-height: 0.95;
|
|
1826
|
+
margin: 0.65rem 0 1.4rem;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
.commerce-lede {
|
|
1830
|
+
color: #555149;
|
|
1831
|
+
font-size: 1.2rem;
|
|
1832
|
+
line-height: 1.65;
|
|
1833
|
+
max-width: 42rem;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
.commerce-facts {
|
|
1837
|
+
display: grid;
|
|
1838
|
+
gap: 1rem;
|
|
1839
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
1840
|
+
margin: 2rem 0;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
.commerce-fact,
|
|
1844
|
+
.commerce-card,
|
|
1845
|
+
.commerce-cart-panel {
|
|
1846
|
+
background: rgba(255, 255, 255, 0.92);
|
|
1847
|
+
border-radius: 1rem;
|
|
1848
|
+
box-shadow: 0 0.5rem 1.25rem rgba(25, 20, 12, 0.08);
|
|
1849
|
+
padding: 1.25rem;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
.commerce-fact span,
|
|
1853
|
+
.commerce-card span {
|
|
1854
|
+
color: #767067;
|
|
1855
|
+
display: block;
|
|
1856
|
+
font-weight: 750;
|
|
1857
|
+
margin-bottom: 0.45rem;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
.commerce-fact strong {
|
|
1861
|
+
font-size: 1.1rem;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
.commerce-checkout {
|
|
1865
|
+
align-items: center;
|
|
1866
|
+
display: flex;
|
|
1867
|
+
flex-wrap: wrap;
|
|
1868
|
+
gap: 0.75rem;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
.commerce-section-title {
|
|
1872
|
+
font-size: 1.8rem;
|
|
1873
|
+
margin: 4.5rem 0 1.5rem;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
.commerce-grid {
|
|
1877
|
+
display: grid;
|
|
1878
|
+
gap: 1rem;
|
|
1879
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
.commerce-card strong {
|
|
1883
|
+
display: block;
|
|
1884
|
+
font-size: 1.45rem;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
.commerce-cart-panel {
|
|
1888
|
+
margin-top: 2rem;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
.commerce-cart-line {
|
|
1892
|
+
align-items: center;
|
|
1893
|
+
border-top: 0.0625rem solid rgba(23, 23, 23, 0.12);
|
|
1894
|
+
display: grid;
|
|
1895
|
+
gap: 1rem;
|
|
1896
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
1897
|
+
padding: 1rem 0;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
.commerce-cart-line:first-of-type {
|
|
1901
|
+
border-top: 0;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
.commerce-quantity {
|
|
1905
|
+
align-items: center;
|
|
1906
|
+
display: flex;
|
|
1907
|
+
gap: 0.45rem;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
.commerce-quantity-button {
|
|
1911
|
+
min-height: 2rem;
|
|
1912
|
+
min-width: 2rem;
|
|
1913
|
+
padding: 0.25rem;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
.commerce-boundary-toggle {
|
|
1917
|
+
align-items: center;
|
|
1918
|
+
background: rgba(255, 255, 255, 0.92);
|
|
1919
|
+
border-radius: 0.8rem;
|
|
1920
|
+
bottom: 1.5rem;
|
|
1921
|
+
box-shadow: 0 0.75rem 2rem rgba(18, 15, 10, 0.14);
|
|
1922
|
+
display: flex;
|
|
1923
|
+
gap: 0.65rem;
|
|
1924
|
+
left: 1.5rem;
|
|
1925
|
+
padding: 0.8rem 1rem;
|
|
1926
|
+
position: fixed;
|
|
1927
|
+
z-index: 80;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
.commerce-boundary-toggle input {
|
|
1931
|
+
accent-color: #00624b;
|
|
1932
|
+
height: 1rem;
|
|
1933
|
+
width: 1rem;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
.boundary-overlay {
|
|
1937
|
+
inset: 0;
|
|
1938
|
+
pointer-events: none;
|
|
1939
|
+
position: fixed;
|
|
1940
|
+
z-index: 70;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
.boundary-overlay__box {
|
|
1944
|
+
border: 0.0625rem solid var(--boundary-color);
|
|
1945
|
+
border-radius: 0.55rem;
|
|
1946
|
+
box-shadow:
|
|
1947
|
+
0 0 0 0.0625rem rgba(255, 255, 255, 0.72),
|
|
1948
|
+
0 0.35rem 1.25rem color-mix(in srgb, var(--boundary-color) 20%, transparent);
|
|
1949
|
+
position: fixed;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
.boundary-overlay__label {
|
|
1953
|
+
background: color-mix(in srgb, var(--boundary-color) 88%, white);
|
|
1954
|
+
border-radius: 999px;
|
|
1955
|
+
color: #0b0a08;
|
|
1956
|
+
font-size: 0.7rem;
|
|
1957
|
+
font-weight: 850;
|
|
1958
|
+
line-height: 1;
|
|
1959
|
+
padding: 0.3rem 0.55rem;
|
|
1960
|
+
position: absolute;
|
|
1961
|
+
right: 0.35rem;
|
|
1962
|
+
top: 0.35rem;
|
|
1963
|
+
white-space: nowrap;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
.boundary-overlay__box[data-label-placement="above"] .boundary-overlay__label {
|
|
1967
|
+
bottom: calc(100% + 0.25rem);
|
|
1968
|
+
top: auto;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
@media (max-width: 860px) {
|
|
1972
|
+
.commerce-header,
|
|
1973
|
+
.commerce-footer,
|
|
1974
|
+
.commerce-product,
|
|
1975
|
+
.commerce-grid,
|
|
1976
|
+
.commerce-facts {
|
|
1977
|
+
grid-template-columns: 1fr;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
.commerce-header,
|
|
1981
|
+
.commerce-footer {
|
|
1982
|
+
align-items: flex-start;
|
|
1983
|
+
flex-direction: column;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
.commerce-product-media {
|
|
1987
|
+
min-height: 20rem;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1706
1990
|
`;
|
|
1707
1991
|
}
|
|
1708
1992
|
function createPostcssConfig() {
|
|
@@ -1759,7 +2043,6 @@ const LocalizedHead = () => {
|
|
|
1759
2043
|
function createShellPage() {
|
|
1760
2044
|
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
1761
2045
|
import { ultramodernUiMarker } from '../../ultramodern-build';
|
|
1762
|
-
import '../index.css';
|
|
1763
2046
|
|
|
1764
2047
|
const languageCodes = ['en', 'cs'] as const;
|
|
1765
2048
|
|
|
@@ -1800,6 +2083,7 @@ export default function ShellHome() {
|
|
|
1800
2083
|
`;
|
|
1801
2084
|
}
|
|
1802
2085
|
function createRemotePage(app) {
|
|
2086
|
+
if ('remote-commerce' === app.id) return createCommerceRemotePage(app);
|
|
1803
2087
|
const effectBffImport = appHasEffectApi(app) ? `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
1804
2088
|
import { useEffect, useState } from 'react';
|
|
1805
2089
|
import { ultramodernUiMarker } from '../../ultramodern-build';
|
|
@@ -1830,8 +2114,7 @@ import { ultramodernUiMarker } from '../../ultramodern-build';
|
|
|
1830
2114
|
` : '';
|
|
1831
2115
|
const effectBffMarkup = appHasEffectApi(app) ? ` <p data-testid="effect-bff-status">{effectApiStatus}</p>
|
|
1832
2116
|
` : '';
|
|
1833
|
-
return `${effectBffImport}
|
|
1834
|
-
|
|
2117
|
+
return `${effectBffImport}
|
|
1835
2118
|
${createLocalizedHeadComponent()}
|
|
1836
2119
|
export default function ${toPascalCase(app.id)}Home() {
|
|
1837
2120
|
const { i18nInstance, language } = useModernI18n();
|
|
@@ -1860,6 +2143,399 @@ ${effectBffMarkup} </main>
|
|
|
1860
2143
|
}
|
|
1861
2144
|
`;
|
|
1862
2145
|
}
|
|
2146
|
+
function createCommerceRemotePage(app) {
|
|
2147
|
+
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2148
|
+
import { useEffect, useState, type CSSProperties } from 'react';
|
|
2149
|
+
import { ultramodernUiMarker } from '../../ultramodern-build';
|
|
2150
|
+
|
|
2151
|
+
const languageCodes = ['en', 'cs'] as const;
|
|
2152
|
+
|
|
2153
|
+
const boundaryDefinitions = [
|
|
2154
|
+
{
|
|
2155
|
+
color: '#ff5a57',
|
|
2156
|
+
id: 'explore',
|
|
2157
|
+
labelKey: 'commerce.boundaries.explore',
|
|
2158
|
+
},
|
|
2159
|
+
{
|
|
2160
|
+
color: '#24d671',
|
|
2161
|
+
id: 'decide',
|
|
2162
|
+
labelKey: 'commerce.boundaries.decide',
|
|
2163
|
+
},
|
|
2164
|
+
{
|
|
2165
|
+
color: '#f4d044',
|
|
2166
|
+
id: 'checkout',
|
|
2167
|
+
labelKey: 'commerce.boundaries.checkout',
|
|
2168
|
+
},
|
|
2169
|
+
] as const;
|
|
2170
|
+
|
|
2171
|
+
type BoundaryId = (typeof boundaryDefinitions)[number]['id'];
|
|
2172
|
+
type BoundaryDefinition = (typeof boundaryDefinitions)[number];
|
|
2173
|
+
|
|
2174
|
+
const boundaryMetadata: Record<BoundaryId, BoundaryDefinition> = {
|
|
2175
|
+
checkout: boundaryDefinitions[2],
|
|
2176
|
+
decide: boundaryDefinitions[1],
|
|
2177
|
+
explore: boundaryDefinitions[0],
|
|
2178
|
+
};
|
|
2179
|
+
|
|
2180
|
+
const products = [
|
|
2181
|
+
{
|
|
2182
|
+
id: 'field-loader-112',
|
|
2183
|
+
titleKey: 'commerce.products.fieldLoader.title',
|
|
2184
|
+
descriptionKey: 'commerce.products.fieldLoader.description',
|
|
2185
|
+
priceKey: 'commerce.products.fieldLoader.price',
|
|
2186
|
+
powerKey: 'commerce.products.fieldLoader.power',
|
|
2187
|
+
availabilityKey: 'commerce.products.fieldLoader.availability',
|
|
2188
|
+
},
|
|
2189
|
+
{
|
|
2190
|
+
id: 'orchard-tractor',
|
|
2191
|
+
titleKey: 'commerce.products.orchard.title',
|
|
2192
|
+
badgeKey: 'commerce.products.orchard.badge',
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
id: 'autonomy-kit',
|
|
2196
|
+
titleKey: 'commerce.products.autonomy.title',
|
|
2197
|
+
badgeKey: 'commerce.products.autonomy.badge',
|
|
2198
|
+
},
|
|
2199
|
+
] as const;
|
|
2200
|
+
|
|
2201
|
+
type ProductId = (typeof products)[number]['id'];
|
|
2202
|
+
type CartState = Partial<Record<ProductId, number>>;
|
|
2203
|
+
|
|
2204
|
+
type BoundaryLabels = Record<BoundaryId, string>;
|
|
2205
|
+
type BoundaryBox = {
|
|
2206
|
+
color: string;
|
|
2207
|
+
height: number;
|
|
2208
|
+
id: BoundaryId;
|
|
2209
|
+
label: string;
|
|
2210
|
+
labelPlacement: 'above' | 'inside';
|
|
2211
|
+
left: number;
|
|
2212
|
+
top: number;
|
|
2213
|
+
width: number;
|
|
2214
|
+
};
|
|
2215
|
+
|
|
2216
|
+
const featuredProduct = products[0];
|
|
2217
|
+
const recommendations = [products[1], products[2]] as const;
|
|
2218
|
+
|
|
2219
|
+
${createLocalizedHeadComponent()}
|
|
2220
|
+
const isBoundaryId = (value: string): value is BoundaryId =>
|
|
2221
|
+
Object.prototype.hasOwnProperty.call(boundaryMetadata, value);
|
|
2222
|
+
|
|
2223
|
+
function collectBoundaryBoxes(labels: BoundaryLabels): BoundaryBox[] {
|
|
2224
|
+
return Array.from(
|
|
2225
|
+
document.querySelectorAll<HTMLElement>('[data-boundary], [data-boundary-page]'),
|
|
2226
|
+
)
|
|
2227
|
+
.map(element => {
|
|
2228
|
+
const id = element.dataset.boundary ?? element.dataset.boundaryPage;
|
|
2229
|
+
|
|
2230
|
+
if (id === undefined || !isBoundaryId(id)) {
|
|
2231
|
+
return undefined;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
const rect = element.getBoundingClientRect();
|
|
2235
|
+
|
|
2236
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
2237
|
+
return undefined;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
return {
|
|
2241
|
+
color: boundaryMetadata[id].color,
|
|
2242
|
+
height: rect.height,
|
|
2243
|
+
id,
|
|
2244
|
+
label: labels[id],
|
|
2245
|
+
labelPlacement: rect.top > 28 ? 'above' : 'inside',
|
|
2246
|
+
left: rect.left,
|
|
2247
|
+
top: rect.top,
|
|
2248
|
+
width: rect.width,
|
|
2249
|
+
};
|
|
2250
|
+
})
|
|
2251
|
+
.filter((box): box is BoundaryBox => box !== undefined);
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
function BoundaryOverlay({
|
|
2255
|
+
labels,
|
|
2256
|
+
visible,
|
|
2257
|
+
}: {
|
|
2258
|
+
labels: BoundaryLabels;
|
|
2259
|
+
visible: boolean;
|
|
2260
|
+
}) {
|
|
2261
|
+
const [boxes, setBoxes] = useState<BoundaryBox[]>([]);
|
|
2262
|
+
|
|
2263
|
+
useEffect(() => {
|
|
2264
|
+
if (!visible) {
|
|
2265
|
+
setBoxes([]);
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
let animationFrame = 0;
|
|
2270
|
+
const update = () => {
|
|
2271
|
+
cancelAnimationFrame(animationFrame);
|
|
2272
|
+
animationFrame = requestAnimationFrame(() => {
|
|
2273
|
+
setBoxes(collectBoundaryBoxes(labels));
|
|
2274
|
+
});
|
|
2275
|
+
};
|
|
2276
|
+
const observer = new ResizeObserver(update);
|
|
2277
|
+
|
|
2278
|
+
observer.observe(document.body);
|
|
2279
|
+
update();
|
|
2280
|
+
window.addEventListener('resize', update);
|
|
2281
|
+
window.addEventListener('scroll', update, true);
|
|
2282
|
+
|
|
2283
|
+
return () => {
|
|
2284
|
+
cancelAnimationFrame(animationFrame);
|
|
2285
|
+
observer.disconnect();
|
|
2286
|
+
window.removeEventListener('resize', update);
|
|
2287
|
+
window.removeEventListener('scroll', update, true);
|
|
2288
|
+
};
|
|
2289
|
+
}, [labels, visible]);
|
|
2290
|
+
|
|
2291
|
+
if (!visible) {
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
return (
|
|
2296
|
+
<div aria-hidden="true" className="boundary-overlay">
|
|
2297
|
+
{boxes.map((box, index) => (
|
|
2298
|
+
<div
|
|
2299
|
+
className="boundary-overlay__box"
|
|
2300
|
+
data-boundary-id={box.id}
|
|
2301
|
+
data-label-placement={box.labelPlacement}
|
|
2302
|
+
key={\`\${box.id}-\${index}\`}
|
|
2303
|
+
style={{
|
|
2304
|
+
'--boundary-color': box.color,
|
|
2305
|
+
height: box.height,
|
|
2306
|
+
left: box.left,
|
|
2307
|
+
top: box.top,
|
|
2308
|
+
width: box.width,
|
|
2309
|
+
} as CSSProperties}
|
|
2310
|
+
>
|
|
2311
|
+
<span className="boundary-overlay__label">{box.label}</span>
|
|
2312
|
+
</div>
|
|
2313
|
+
))}
|
|
2314
|
+
</div>
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
export default function ${toPascalCase(app.id)}Home() {
|
|
2319
|
+
const { i18nInstance, language } = useModernI18n();
|
|
2320
|
+
const t = i18nInstance.t.bind(i18nInstance);
|
|
2321
|
+
const [cart, setCart] = useState<CartState>({});
|
|
2322
|
+
const [showBoundaries, setShowBoundaries] = useState(false);
|
|
2323
|
+
const [effectApiStatus, setEffectApiStatus] = useState('pending');
|
|
2324
|
+
const boundaryLabels = {
|
|
2325
|
+
checkout: t('commerce.boundaries.checkout'),
|
|
2326
|
+
decide: t('commerce.boundaries.decide'),
|
|
2327
|
+
explore: t('commerce.boundaries.explore'),
|
|
2328
|
+
} satisfies BoundaryLabels;
|
|
2329
|
+
const cartLines = products
|
|
2330
|
+
.map(product => ({
|
|
2331
|
+
product,
|
|
2332
|
+
quantity: cart[product.id] ?? 0,
|
|
2333
|
+
}))
|
|
2334
|
+
.filter(line => line.quantity > 0);
|
|
2335
|
+
const cartCount = cartLines.reduce((total, line) => total + line.quantity, 0);
|
|
2336
|
+
|
|
2337
|
+
useEffect(() => {
|
|
2338
|
+
void fetch('${effectApiPrefix(app)}/effect/${effectApiStem(app)}?limit=1', {
|
|
2339
|
+
headers: {
|
|
2340
|
+
accept: 'application/json',
|
|
2341
|
+
},
|
|
2342
|
+
})
|
|
2343
|
+
.then(response => {
|
|
2344
|
+
if (!response.ok) {
|
|
2345
|
+
throw new Error(\`Effect BFF request failed: \${response.status}\`);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
return response.json() as Promise<{ items?: Array<{ title?: string }> }>;
|
|
2349
|
+
})
|
|
2350
|
+
.then(data => {
|
|
2351
|
+
setEffectApiStatus(data.items[0]?.title ?? 'empty');
|
|
2352
|
+
})
|
|
2353
|
+
.catch(() => {
|
|
2354
|
+
setEffectApiStatus('unavailable');
|
|
2355
|
+
});
|
|
2356
|
+
}, []);
|
|
2357
|
+
|
|
2358
|
+
const addToCart = (id: ProductId) => {
|
|
2359
|
+
setCart(current => ({
|
|
2360
|
+
...current,
|
|
2361
|
+
[id]: (current[id] ?? 0) + 1,
|
|
2362
|
+
}));
|
|
2363
|
+
};
|
|
2364
|
+
|
|
2365
|
+
const reduceQuantity = (id: ProductId) => {
|
|
2366
|
+
setCart(current => {
|
|
2367
|
+
const quantity = current[id] ?? 0;
|
|
2368
|
+
const next = { ...current };
|
|
2369
|
+
|
|
2370
|
+
if (quantity <= 1) {
|
|
2371
|
+
delete next[id];
|
|
2372
|
+
} else {
|
|
2373
|
+
next[id] = quantity - 1;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
return next;
|
|
2377
|
+
});
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
const removeFromCart = (id: ProductId) => {
|
|
2381
|
+
setCart(current => {
|
|
2382
|
+
const next = { ...current };
|
|
2383
|
+
|
|
2384
|
+
delete next[id];
|
|
2385
|
+
return next;
|
|
2386
|
+
});
|
|
2387
|
+
};
|
|
2388
|
+
|
|
2389
|
+
return (
|
|
2390
|
+
<main className="commerce-shell">
|
|
2391
|
+
<LocalizedHead />
|
|
2392
|
+
<BoundaryOverlay labels={boundaryLabels} visible={showBoundaries} />
|
|
2393
|
+
<header className="commerce-header" data-boundary="explore">
|
|
2394
|
+
<strong className="commerce-logo">{t('commerce.brand')}</strong>
|
|
2395
|
+
<nav aria-label={t('commerce.navigation.primary')} className="commerce-nav">
|
|
2396
|
+
<a className="commerce-pill" href="#machines">
|
|
2397
|
+
{t('commerce.navigation.machines')}
|
|
2398
|
+
</a>
|
|
2399
|
+
<a className="commerce-pill" href="#checkout">
|
|
2400
|
+
{t('commerce.navigation.checkout')}
|
|
2401
|
+
</a>
|
|
2402
|
+
</nav>
|
|
2403
|
+
<div className="commerce-actions">
|
|
2404
|
+
<a className="commerce-cart-button" data-boundary="checkout" href="#cart">
|
|
2405
|
+
{t('commerce.cart.button', { count: cartCount })}
|
|
2406
|
+
</a>
|
|
2407
|
+
<nav aria-label={t('commerce.language.switcher')} className="commerce-language">
|
|
2408
|
+
{languageCodes.map(code => (
|
|
2409
|
+
<a
|
|
2410
|
+
aria-current={language === code ? 'page' : undefined}
|
|
2411
|
+
className="commerce-pill"
|
|
2412
|
+
href={\`/\${code}\`}
|
|
2413
|
+
key={code}
|
|
2414
|
+
>
|
|
2415
|
+
{t(\`commerce.language.\${code}\`)}
|
|
2416
|
+
</a>
|
|
2417
|
+
))}
|
|
2418
|
+
</nav>
|
|
2419
|
+
</div>
|
|
2420
|
+
</header>
|
|
2421
|
+
|
|
2422
|
+
<div className="commerce-page">
|
|
2423
|
+
<section className="commerce-product" data-boundary-page="decide" id="machines">
|
|
2424
|
+
<div
|
|
2425
|
+
aria-label={t('commerce.products.fieldLoader.imageAlt')}
|
|
2426
|
+
className="commerce-product-media"
|
|
2427
|
+
role="img"
|
|
2428
|
+
/>
|
|
2429
|
+
<div>
|
|
2430
|
+
<p className="commerce-eyebrow">{t('commerce.detail.eyebrow')}</p>
|
|
2431
|
+
<h1 className="commerce-title">{t(featuredProduct.titleKey)}</h1>
|
|
2432
|
+
<p className="commerce-lede">{t(featuredProduct.descriptionKey)}</p>
|
|
2433
|
+
<div className="commerce-facts">
|
|
2434
|
+
<div className="commerce-fact">
|
|
2435
|
+
<span>{t('commerce.detail.price')}</span>
|
|
2436
|
+
<strong>{t(featuredProduct.priceKey)}</strong>
|
|
2437
|
+
</div>
|
|
2438
|
+
<div className="commerce-fact">
|
|
2439
|
+
<span>{t('commerce.detail.power')}</span>
|
|
2440
|
+
<strong>{t(featuredProduct.powerKey)}</strong>
|
|
2441
|
+
</div>
|
|
2442
|
+
<div className="commerce-fact">
|
|
2443
|
+
<span>{t('commerce.detail.availability')}</span>
|
|
2444
|
+
<strong>{t(featuredProduct.availabilityKey)}</strong>
|
|
2445
|
+
</div>
|
|
2446
|
+
</div>
|
|
2447
|
+
<div className="commerce-checkout" data-boundary="checkout" id="checkout">
|
|
2448
|
+
<button
|
|
2449
|
+
className="commerce-button"
|
|
2450
|
+
onClick={() => addToCart(featuredProduct.id)}
|
|
2451
|
+
type="button"
|
|
2452
|
+
>
|
|
2453
|
+
{t('commerce.cart.add')}
|
|
2454
|
+
</button>
|
|
2455
|
+
<a className="commerce-link-button" href="#cart">
|
|
2456
|
+
{t('commerce.cart.view')}
|
|
2457
|
+
</a>
|
|
2458
|
+
</div>
|
|
2459
|
+
</div>
|
|
2460
|
+
</section>
|
|
2461
|
+
|
|
2462
|
+
<section data-boundary="explore">
|
|
2463
|
+
<h2 className="commerce-section-title">{t('commerce.recommendations.title')}</h2>
|
|
2464
|
+
<div className="commerce-grid">
|
|
2465
|
+
{recommendations.map(product => (
|
|
2466
|
+
<article className="commerce-card" key={product.id}>
|
|
2467
|
+
<span>{t(product.badgeKey)}</span>
|
|
2468
|
+
<strong>{t(product.titleKey)}</strong>
|
|
2469
|
+
</article>
|
|
2470
|
+
))}
|
|
2471
|
+
</div>
|
|
2472
|
+
</section>
|
|
2473
|
+
|
|
2474
|
+
<section className="commerce-cart-panel" data-boundary="checkout" id="cart">
|
|
2475
|
+
<h2>{t('commerce.cart.title')}</h2>
|
|
2476
|
+
{cartLines.length === 0 ? (
|
|
2477
|
+
<p>{t('commerce.cart.empty')}</p>
|
|
2478
|
+
) : (
|
|
2479
|
+
cartLines.map(line => (
|
|
2480
|
+
<div className="commerce-cart-line" key={line.product.id}>
|
|
2481
|
+
<strong>{t(line.product.titleKey)}</strong>
|
|
2482
|
+
<div className="commerce-quantity">
|
|
2483
|
+
<button
|
|
2484
|
+
aria-label={t('commerce.cart.decrease', {
|
|
2485
|
+
name: t(line.product.titleKey),
|
|
2486
|
+
})}
|
|
2487
|
+
className="commerce-quantity-button"
|
|
2488
|
+
onClick={() => reduceQuantity(line.product.id)}
|
|
2489
|
+
type="button"
|
|
2490
|
+
>
|
|
2491
|
+
-
|
|
2492
|
+
</button>
|
|
2493
|
+
<span>{line.quantity}</span>
|
|
2494
|
+
<button
|
|
2495
|
+
aria-label={t('commerce.cart.increase', {
|
|
2496
|
+
name: t(line.product.titleKey),
|
|
2497
|
+
})}
|
|
2498
|
+
className="commerce-quantity-button"
|
|
2499
|
+
onClick={() => addToCart(line.product.id)}
|
|
2500
|
+
type="button"
|
|
2501
|
+
>
|
|
2502
|
+
+
|
|
2503
|
+
</button>
|
|
2504
|
+
<button
|
|
2505
|
+
className="commerce-link-button"
|
|
2506
|
+
onClick={() => removeFromCart(line.product.id)}
|
|
2507
|
+
type="button"
|
|
2508
|
+
>
|
|
2509
|
+
{t('commerce.cart.remove')}
|
|
2510
|
+
</button>
|
|
2511
|
+
</div>
|
|
2512
|
+
</div>
|
|
2513
|
+
))
|
|
2514
|
+
)}
|
|
2515
|
+
</section>
|
|
2516
|
+
</div>
|
|
2517
|
+
|
|
2518
|
+
<footer className="commerce-footer" data-boundary="explore">
|
|
2519
|
+
<span>{t('commerce.footer.stack')}</span>
|
|
2520
|
+
<span data-testid="effect-bff-status">{effectApiStatus}</span>
|
|
2521
|
+
<span data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
|
|
2522
|
+
{ultramodernUiMarker.appId}:{ultramodernUiMarker.version}
|
|
2523
|
+
</span>
|
|
2524
|
+
</footer>
|
|
2525
|
+
|
|
2526
|
+
<label className="commerce-boundary-toggle">
|
|
2527
|
+
<input
|
|
2528
|
+
checked={showBoundaries}
|
|
2529
|
+
onChange={event => setShowBoundaries(event.currentTarget.checked)}
|
|
2530
|
+
type="checkbox"
|
|
2531
|
+
/>
|
|
2532
|
+
{t('commerce.boundaries.toggle')}
|
|
2533
|
+
</label>
|
|
2534
|
+
</main>
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
`;
|
|
2538
|
+
}
|
|
1863
2539
|
function createLayout(appId) {
|
|
1864
2540
|
return `import { Outlet } from '@modern-js/plugin-tanstack/runtime';
|
|
1865
2541
|
import './index.css';
|
|
@@ -1894,7 +2570,7 @@ function createAppLocaleMessages(app, language) {
|
|
|
1894
2570
|
const czechLabels = {
|
|
1895
2571
|
commerce: {
|
|
1896
2572
|
role: 'obchod',
|
|
1897
|
-
title: '
|
|
2573
|
+
title: 'Obchodní remote'
|
|
1898
2574
|
},
|
|
1899
2575
|
'design-system': {
|
|
1900
2576
|
role: 'design system',
|
|
@@ -1902,20 +2578,20 @@ function createAppLocaleMessages(app, language) {
|
|
|
1902
2578
|
},
|
|
1903
2579
|
identity: {
|
|
1904
2580
|
role: 'identita',
|
|
1905
|
-
title: '
|
|
2581
|
+
title: 'Identitní remote'
|
|
1906
2582
|
}
|
|
1907
2583
|
};
|
|
1908
2584
|
if ('shell' === app.kind) return {
|
|
1909
2585
|
shell: {
|
|
1910
2586
|
language: {
|
|
1911
|
-
cs: 'en' === language ? 'Czech' : '
|
|
1912
|
-
en: 'en' === language ? 'English' : '
|
|
2587
|
+
cs: 'en' === language ? 'Czech' : 'Čeština',
|
|
2588
|
+
en: 'en' === language ? 'English' : 'Angličtina',
|
|
1913
2589
|
switcher: 'en' === language ? 'Language' : 'Jazyk'
|
|
1914
2590
|
},
|
|
1915
2591
|
remotes: {
|
|
1916
|
-
commerce: 'en' === language ? 'Commerce Remote' : '
|
|
2592
|
+
commerce: 'en' === language ? 'Commerce Remote' : 'Obchodní remote',
|
|
1917
2593
|
designSystem: 'en' === language ? 'Design System Remote' : 'Design system remote',
|
|
1918
|
-
identity: 'en' === language ? 'Identity Remote' : '
|
|
2594
|
+
identity: 'en' === language ? 'Identity Remote' : 'Identitní remote'
|
|
1919
2595
|
},
|
|
1920
2596
|
title: 'en' === language ? 'UltraModern SuperApp Shell' : 'UltraModern SuperApp shell'
|
|
1921
2597
|
}
|
|
@@ -1925,11 +2601,74 @@ function createAppLocaleMessages(app, language) {
|
|
|
1925
2601
|
role: domain,
|
|
1926
2602
|
title: `${app.displayName} CZ`
|
|
1927
2603
|
};
|
|
2604
|
+
if ('commerce' === domain) return {
|
|
2605
|
+
commerce: {
|
|
2606
|
+
boundaries: {
|
|
2607
|
+
checkout: 'en' === language ? 'checkout' : 'pokladna',
|
|
2608
|
+
decide: 'en' === language ? 'decide' : 'rozhodování',
|
|
2609
|
+
explore: 'en' === language ? 'explore' : 'procházení',
|
|
2610
|
+
toggle: 'en' === language ? 'show team boundaries' : 'zobrazit hranice týmů'
|
|
2611
|
+
},
|
|
2612
|
+
brand: 'Acre & Iron',
|
|
2613
|
+
cart: {
|
|
2614
|
+
add: 'en' === language ? 'Add to cart' : 'Přidat do košíku',
|
|
2615
|
+
button: 'en' === language ? 'Your cart ({{count}})' : 'Košík ({{count}})',
|
|
2616
|
+
decrease: 'en' === language ? 'Decrease {{name}} quantity' : 'Snížit množství položky {{name}}',
|
|
2617
|
+
empty: 'en' === language ? 'Your cart is empty.' : 'Košík je prázdný.',
|
|
2618
|
+
increase: 'en' === language ? 'Increase {{name}} quantity' : 'Zvýšit množství položky {{name}}',
|
|
2619
|
+
remove: 'en' === language ? 'Remove' : 'Odebrat',
|
|
2620
|
+
title: 'en' === language ? 'Cart' : 'Košík',
|
|
2621
|
+
view: 'en' === language ? 'View cart' : 'Zobrazit košík'
|
|
2622
|
+
},
|
|
2623
|
+
detail: {
|
|
2624
|
+
availability: 'en' === language ? 'Availability' : 'Dostupnost',
|
|
2625
|
+
eyebrow: 'en' === language ? 'Machine detail' : 'Detail stroje',
|
|
2626
|
+
power: 'en' === language ? 'Power' : 'Výkon',
|
|
2627
|
+
price: 'en' === language ? 'Price' : 'Cena'
|
|
2628
|
+
},
|
|
2629
|
+
footer: {
|
|
2630
|
+
stack: 'en' === language ? 'SPA, SSR-ready Module Federation, React, Effect BFF' : 'SPA, SSR-ready Module Federation, React, Effect BFF'
|
|
2631
|
+
},
|
|
2632
|
+
language: {
|
|
2633
|
+
cs: 'en' === language ? 'Czech' : 'Čeština',
|
|
2634
|
+
en: 'en' === language ? 'English' : 'Angličtina',
|
|
2635
|
+
switcher: 'en' === language ? 'Language' : 'Jazyk'
|
|
2636
|
+
},
|
|
2637
|
+
navigation: {
|
|
2638
|
+
checkout: 'en' === language ? 'Checkout' : 'Pokladna',
|
|
2639
|
+
machines: 'en' === language ? 'Machines' : 'Stroje',
|
|
2640
|
+
primary: 'en' === language ? 'Primary commerce navigation' : 'Hlavní navigace obchodu'
|
|
2641
|
+
},
|
|
2642
|
+
products: {
|
|
2643
|
+
autonomy: {
|
|
2644
|
+
badge: 'en' === language ? 'AI-first option' : 'AI varianta',
|
|
2645
|
+
title: 'en' === language ? 'Autonomy Retrofit Kit' : 'Sada pro autonomní řízení'
|
|
2646
|
+
},
|
|
2647
|
+
fieldLoader: {
|
|
2648
|
+
availability: 'en' === language ? 'In stock' : 'Skladem',
|
|
2649
|
+
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.',
|
|
2650
|
+
imageAlt: 'en' === language ? 'Field Loader 112 tractor working on a bright farm lane' : 'Traktor Field Loader 112 pracuje na světlé polní cestě',
|
|
2651
|
+
power: '112 hp',
|
|
2652
|
+
price: 'EUR 42,500',
|
|
2653
|
+
title: 'Field Loader 112'
|
|
2654
|
+
},
|
|
2655
|
+
orchard: {
|
|
2656
|
+
badge: 'en' === language ? 'Best for tight rows' : 'Nejlepší do úzkých řádků',
|
|
2657
|
+
title: 'en' === language ? 'Narrow Orchard Tractor' : 'Úzký sadový traktor'
|
|
2658
|
+
}
|
|
2659
|
+
},
|
|
2660
|
+
recommendations: {
|
|
2661
|
+
title: 'en' === language ? 'Compare alternatives' : 'Porovnat alternativy'
|
|
2662
|
+
},
|
|
2663
|
+
role: 'en' === language ? 'commerce' : 'obchod',
|
|
2664
|
+
title: 'en' === language ? app.displayName : czechLabel.title
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
1928
2667
|
return {
|
|
1929
2668
|
[domain]: {
|
|
1930
2669
|
language: {
|
|
1931
|
-
cs: 'en' === language ? 'Czech' : '
|
|
1932
|
-
en: 'en' === language ? 'English' : '
|
|
2670
|
+
cs: 'en' === language ? 'Czech' : 'Čeština',
|
|
2671
|
+
en: 'en' === language ? 'English' : 'Angličtina',
|
|
1933
2672
|
switcher: 'en' === language ? 'Language' : 'Jazyk'
|
|
1934
2673
|
},
|
|
1935
2674
|
role: 'en' === language ? app.domain ?? app.kind : czechLabel.role,
|
|
@@ -2388,7 +3127,7 @@ function createTopology(scope) {
|
|
|
2388
3127
|
validation: {
|
|
2389
3128
|
script: "scripts/validate-ultramodern-workspace.mjs",
|
|
2390
3129
|
commands: [
|
|
2391
|
-
'pnpm ultramodern:check'
|
|
3130
|
+
'mise exec -- pnpm ultramodern:check'
|
|
2392
3131
|
]
|
|
2393
3132
|
}
|
|
2394
3133
|
};
|
|
@@ -2619,6 +3358,48 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
|
|
|
2619
3358
|
apiSurface: 'effect-bff'
|
|
2620
3359
|
} : {}
|
|
2621
3360
|
},
|
|
3361
|
+
...'commerce' === app.domain ? {
|
|
3362
|
+
boundaryVisualization: {
|
|
3363
|
+
mode: 'overlay',
|
|
3364
|
+
layoutAffecting: false,
|
|
3365
|
+
toggle: 'user-controlled',
|
|
3366
|
+
boundaries: [
|
|
3367
|
+
{
|
|
3368
|
+
id: 'explore',
|
|
3369
|
+
labelKey: 'commerce.boundaries.explore',
|
|
3370
|
+
owner: 'team-explore',
|
|
3371
|
+
color: '#ff5a57',
|
|
3372
|
+
owns: [
|
|
3373
|
+
'header',
|
|
3374
|
+
'footer',
|
|
3375
|
+
'recommendations',
|
|
3376
|
+
'catalog'
|
|
3377
|
+
]
|
|
3378
|
+
},
|
|
3379
|
+
{
|
|
3380
|
+
id: 'decide',
|
|
3381
|
+
labelKey: 'commerce.boundaries.decide',
|
|
3382
|
+
owner: 'team-decide',
|
|
3383
|
+
color: '#24d671',
|
|
3384
|
+
owns: [
|
|
3385
|
+
'product-detail',
|
|
3386
|
+
'variant-selection'
|
|
3387
|
+
]
|
|
3388
|
+
},
|
|
3389
|
+
{
|
|
3390
|
+
id: 'checkout',
|
|
3391
|
+
labelKey: 'commerce.boundaries.checkout',
|
|
3392
|
+
owner: 'team-checkout',
|
|
3393
|
+
color: '#f4d044',
|
|
3394
|
+
owns: [
|
|
3395
|
+
'add-to-cart',
|
|
3396
|
+
'cart-link',
|
|
3397
|
+
'cart-lines'
|
|
3398
|
+
]
|
|
3399
|
+
}
|
|
3400
|
+
]
|
|
3401
|
+
}
|
|
3402
|
+
} : {},
|
|
2622
3403
|
...appHasEffectApi(app) ? {
|
|
2623
3404
|
effect: {
|
|
2624
3405
|
runtime: 'effect',
|
|
@@ -2643,9 +3424,8 @@ function createGeneratedContract(scope, apps = [
|
|
|
2643
3424
|
packageManager: {
|
|
2644
3425
|
source: 'package.json',
|
|
2645
3426
|
manager: 'pnpm',
|
|
2646
|
-
version:
|
|
2647
|
-
toolchain: 'mise'
|
|
2648
|
-
corepack: false
|
|
3427
|
+
version: PNPM_VERSION,
|
|
3428
|
+
toolchain: 'mise'
|
|
2649
3429
|
},
|
|
2650
3430
|
versions: {
|
|
2651
3431
|
typescript: TYPESCRIPT_VERSION,
|
|
@@ -2697,6 +3477,7 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
2697
3477
|
'.agents/**',
|
|
2698
3478
|
'.github/**',
|
|
2699
3479
|
'.gitignore',
|
|
3480
|
+
'.mise.toml',
|
|
2700
3481
|
'.modernjs/**',
|
|
2701
3482
|
'AGENTS.md',
|
|
2702
3483
|
'README.md',
|
|
@@ -2771,8 +3552,9 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
2771
3552
|
'template-manifest-retained'
|
|
2772
3553
|
],
|
|
2773
3554
|
expectedCommands: [
|
|
2774
|
-
'
|
|
2775
|
-
'pnpm
|
|
3555
|
+
'mise install',
|
|
3556
|
+
'mise exec -- pnpm install',
|
|
3557
|
+
'mise exec -- pnpm run ultramodern:check'
|
|
2776
3558
|
]
|
|
2777
3559
|
}
|
|
2778
3560
|
};
|
|
@@ -2815,9 +3597,7 @@ function writeEffectService(targetDir, scope, packageSource, enableTailwind, ser
|
|
|
2815
3597
|
writeFile(targetDir, `${service.directory}/src/modern-app-env.d.ts`, "/// <reference types='@modern-js/app-tools/types' />\n");
|
|
2816
3598
|
writeFile(targetDir, `${service.directory}/src/ultramodern-build.ts`, createUltramodernBuildModule(scope, service));
|
|
2817
3599
|
writeFile(targetDir, `${service.directory}/src/routes/layout.tsx`, createLayout(service.id));
|
|
2818
|
-
writeFile(targetDir, `${service.directory}/src/routes/page.tsx`, `
|
|
2819
|
-
|
|
2820
|
-
export default function ${toPascalCase(service.id)}Home() {
|
|
3600
|
+
writeFile(targetDir, `${service.directory}/src/routes/page.tsx`, `export default function ${toPascalCase(service.id)}Home() {
|
|
2821
3601
|
return <main>${service.id} Effect service</main>;
|
|
2822
3602
|
}
|
|
2823
3603
|
`);
|
|
@@ -3143,6 +3923,7 @@ function generateUltramodernWorkspace(options) {
|
|
|
3143
3923
|
copyRootTemplate(options.targetDir, {
|
|
3144
3924
|
packageName: options.packageName,
|
|
3145
3925
|
packageScope: scope,
|
|
3926
|
+
pnpmVersion: PNPM_VERSION,
|
|
3146
3927
|
tailwindEnabled: String(enableTailwind)
|
|
3147
3928
|
});
|
|
3148
3929
|
writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource));
|
|
@@ -3171,6 +3952,7 @@ const packageNamePattern = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
|
|
|
3171
3952
|
const src_TANSTACK_ROUTER_VERSION = '1.170.8';
|
|
3172
3953
|
const src_TAILWIND_VERSION = '4.3.0';
|
|
3173
3954
|
const src_TAILWIND_POSTCSS_VERSION = '4.3.0';
|
|
3955
|
+
const src_PNPM_VERSION = '11.4.0';
|
|
3174
3956
|
const requiredDeniedPaths = [
|
|
3175
3957
|
'.git/**',
|
|
3176
3958
|
'.npmrc',
|
|
@@ -3411,9 +4193,10 @@ function createBuiltinTemplateManifest(version) {
|
|
|
3411
4193
|
'template-manifest-retained'
|
|
3412
4194
|
],
|
|
3413
4195
|
expectedCommands: [
|
|
3414
|
-
'
|
|
3415
|
-
'pnpm
|
|
3416
|
-
'pnpm
|
|
4196
|
+
'mise install',
|
|
4197
|
+
'mise exec -- pnpm install',
|
|
4198
|
+
'mise exec -- pnpm test',
|
|
4199
|
+
'mise exec -- pnpm run ultramodern:check'
|
|
3417
4200
|
]
|
|
3418
4201
|
}
|
|
3419
4202
|
};
|
|
@@ -3786,7 +4569,8 @@ async function main() {
|
|
|
3786
4569
|
const dim = '\x1b[2m\x1b[3m';
|
|
3787
4570
|
const reset = '\x1b[0m';
|
|
3788
4571
|
console.log(`${i18n.t(localeKeys.message.success)}\n`);
|
|
3789
|
-
console.log(`${dim}
|
|
4572
|
+
console.log(`${dim} mise install${reset}`);
|
|
4573
|
+
console.log(`${dim} mise exec -- pnpm ultramodern:check${reset}\n`);
|
|
3790
4574
|
return;
|
|
3791
4575
|
}
|
|
3792
4576
|
if (node_fs.existsSync(targetDir)) {
|
|
@@ -3814,8 +4598,9 @@ async function main() {
|
|
|
3814
4598
|
if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
|
|
3815
4599
|
projectName
|
|
3816
4600
|
})}${reset}`);
|
|
4601
|
+
console.log(`${dim} mise install${reset}`);
|
|
3817
4602
|
console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
|
|
3818
|
-
console.log(`${dim} pnpm ultramodern:check${reset}`);
|
|
4603
|
+
console.log(`${dim} mise exec -- pnpm ultramodern:check${reset}`);
|
|
3819
4604
|
console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
|
|
3820
4605
|
return;
|
|
3821
4606
|
}
|
|
@@ -3841,6 +4626,7 @@ async function main() {
|
|
|
3841
4626
|
tanstackRouterVersion: src_TANSTACK_ROUTER_VERSION,
|
|
3842
4627
|
tailwindVersion: src_TAILWIND_VERSION,
|
|
3843
4628
|
tailwindPostcssVersion: src_TAILWIND_POSTCSS_VERSION,
|
|
4629
|
+
pnpmVersion: src_PNPM_VERSION,
|
|
3844
4630
|
isSubproject,
|
|
3845
4631
|
routerFramework,
|
|
3846
4632
|
bffRuntime,
|
|
@@ -3935,6 +4721,7 @@ function copyTemplate(src, dest, options) {
|
|
|
3935
4721
|
tanstackRouterVersion: options.tanstackRouterVersion,
|
|
3936
4722
|
tailwindVersion: options.tailwindVersion,
|
|
3937
4723
|
tailwindPostcssVersion: options.tailwindPostcssVersion,
|
|
4724
|
+
pnpmVersion: options.pnpmVersion,
|
|
3938
4725
|
isSubproject: options.isSubproject,
|
|
3939
4726
|
isTanstackRouter: 'tanstack' === options.routerFramework,
|
|
3940
4727
|
enableBff: 'none' !== options.bffRuntime,
|