@bleedingdev/modern-js-create 3.2.0-ultramodern.59 → 3.2.0-ultramodern.60
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 +857 -1169
- package/dist/types/locale/en.d.ts +1 -0
- package/dist/types/locale/zh.d.ts +1 -0
- package/package.json +3 -3
- package/template/README.md +53 -125
- package/template/config/public/locales/cs/translation.json +10 -10
- package/template/config/public/locales/en/translation.json +10 -10
- package/template/src/routes/[lang]/page.tsx.handlebars +4 -2
- package/template-workspace/AGENTS.md +17 -4
- package/template-workspace/README.md.handlebars +22 -19
- package/template-workspace/scripts/assert-mf-types.mjs +0 -44
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -935
package/dist/index.js
CHANGED
|
@@ -470,11 +470,11 @@ const EN_LOCALE = {
|
|
|
470
470
|
optionBffRuntime: ' --bff-runtime Select BFF runtime (hono or effect)',
|
|
471
471
|
optionTailwind: ' --no-tailwind Disable default Tailwind CSS v4 scaffold',
|
|
472
472
|
optionWorkspace: ' --workspace Use workspace protocol for @modern-js dependencies (for local monorepo testing)',
|
|
473
|
-
optionUltramodernWorkspace: ' --ultramodern-workspace Generate
|
|
474
|
-
optionUltramodernPackageSource: ' --ultramodern-package-source Select UltraModern package source (workspace or install)',
|
|
473
|
+
optionUltramodernWorkspace: ' --ultramodern-workspace Generate an UltraModern SuperApp workspace (explicit opt-in; default is a simple app)',
|
|
474
|
+
optionUltramodernPackageSource: ' --ultramodern-package-source Select UltraModern package source (workspace or install; BleedingDev defaults to install aliases)',
|
|
475
475
|
optionUltramodernPackageScope: ' --ultramodern-package-scope Publish scope for npm alias installs (for example bleedingdev)',
|
|
476
476
|
optionUltramodernPackageNamePrefix: ' --ultramodern-package-name-prefix Prefix for npm alias package names (default: modern-js-)',
|
|
477
|
-
optionVertical: ' --vertical
|
|
477
|
+
optionVertical: ' --vertical Mutate the current existing UltraModern workspace and wire a MicroVertical named <project-name>',
|
|
478
478
|
optionSub: ' -s, --sub Mark as a subproject (package in monorepo)',
|
|
479
479
|
examples: '💡 Examples:',
|
|
480
480
|
example1: ' create my-app',
|
|
@@ -486,8 +486,9 @@ const EN_LOCALE = {
|
|
|
486
486
|
example7: ' create my-app --bff',
|
|
487
487
|
example8: ' create my-app --router tanstack --bff-runtime effect',
|
|
488
488
|
example9: ' create my-app --router tanstack --bff-runtime effect --workspace',
|
|
489
|
-
example10: ' pnpm dlx @bleedingdev/modern-js-create my-
|
|
490
|
-
example11: ' create
|
|
489
|
+
example10: ' pnpm dlx @bleedingdev/modern-js-create my-app',
|
|
490
|
+
example11: ' pnpm dlx @bleedingdev/modern-js-create my-super-app --ultramodern-workspace',
|
|
491
|
+
example12: ' create catalog --vertical',
|
|
491
492
|
moreInfo: '📚 Learn more: https://modernjs.dev'
|
|
492
493
|
},
|
|
493
494
|
version: {
|
|
@@ -527,11 +528,11 @@ const ZH_LOCALE = {
|
|
|
527
528
|
optionBffRuntime: ' --bff-runtime 选择 BFF 运行时(hono 或 effect)',
|
|
528
529
|
optionTailwind: ' --no-tailwind 禁用默认 Tailwind CSS v4 模板',
|
|
529
530
|
optionWorkspace: ' --workspace 对 @modern-js 依赖使用 workspace 协议(用于本地 monorepo 联调)',
|
|
530
|
-
optionUltramodernWorkspace: ' --ultramodern-workspace
|
|
531
|
-
optionUltramodernPackageSource: ' --ultramodern-package-source 选择 UltraModern 依赖来源(workspace 或 install)',
|
|
531
|
+
optionUltramodernWorkspace: ' --ultramodern-workspace 生成 UltraModern SuperApp 工作区(显式选择;默认创建简单应用)',
|
|
532
|
+
optionUltramodernPackageSource: ' --ultramodern-package-source 选择 UltraModern 依赖来源(workspace 或 install;BleedingDev 默认使用 install alias)',
|
|
532
533
|
optionUltramodernPackageScope: ' --ultramodern-package-scope npm alias 安装使用的发布 scope(例如 bleedingdev)',
|
|
533
534
|
optionUltramodernPackageNamePrefix: ' --ultramodern-package-name-prefix npm alias 包名前缀(默认:modern-js-)',
|
|
534
|
-
optionVertical: ' --vertical
|
|
535
|
+
optionVertical: ' --vertical 修改当前已有的 UltraModern 工作区,并接入名为 <项目名称> 的 MicroVertical',
|
|
535
536
|
optionSub: ' -s, --sub 标记为子项目(monorepo 中的子包)',
|
|
536
537
|
examples: '💡 示例:',
|
|
537
538
|
example1: ' create my-app',
|
|
@@ -543,8 +544,9 @@ const ZH_LOCALE = {
|
|
|
543
544
|
example7: ' create my-app --bff',
|
|
544
545
|
example8: ' create my-app --router tanstack --bff-runtime effect',
|
|
545
546
|
example9: ' create my-app --router tanstack --bff-runtime effect --workspace',
|
|
546
|
-
example10: '
|
|
547
|
-
example11: ' create
|
|
547
|
+
example10: ' pnpm dlx @bleedingdev/modern-js-create my-app',
|
|
548
|
+
example11: ' pnpm dlx @bleedingdev/modern-js-create my-super-app --ultramodern-workspace',
|
|
549
|
+
example12: ' create catalog --vertical',
|
|
548
550
|
moreInfo: '📚 更多信息: https://modernjs.dev'
|
|
549
551
|
},
|
|
550
552
|
version: {
|
|
@@ -621,11 +623,7 @@ const shellApp = {
|
|
|
621
623
|
portEnv: 'SHELL_SUPER_APP_PORT',
|
|
622
624
|
port: 3020,
|
|
623
625
|
mfName: 'shellSuperApp',
|
|
624
|
-
verticalRefs: [
|
|
625
|
-
'explore',
|
|
626
|
-
'decide',
|
|
627
|
-
'checkout'
|
|
628
|
-
],
|
|
626
|
+
verticalRefs: [],
|
|
629
627
|
ownership: {
|
|
630
628
|
team: 'super-app-platform',
|
|
631
629
|
slack: '#super-app-platform',
|
|
@@ -641,130 +639,12 @@ const shellApp = {
|
|
|
641
639
|
}
|
|
642
640
|
}
|
|
643
641
|
};
|
|
644
|
-
|
|
645
|
-
{
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
kind: 'vertical',
|
|
651
|
-
domain: 'explore',
|
|
652
|
-
portEnv: 'VERTICAL_EXPLORE_PORT',
|
|
653
|
-
port: 3021,
|
|
654
|
-
mfName: 'verticalExplore',
|
|
655
|
-
exposes: {
|
|
656
|
-
'./Footer': './src/components/footer.tsx',
|
|
657
|
-
'./Header': './src/components/header.tsx',
|
|
658
|
-
'./Recommendations': './src/components/recommendations.tsx',
|
|
659
|
-
'./Route': './src/federation-entry.tsx',
|
|
660
|
-
'./StorePicker': './src/components/store-picker.tsx'
|
|
661
|
-
},
|
|
662
|
-
effectApi: {
|
|
663
|
-
stem: 'explore',
|
|
664
|
-
prefix: '/explore-api',
|
|
665
|
-
consumedBy: [
|
|
666
|
-
shellApp.id,
|
|
667
|
-
'explore'
|
|
668
|
-
]
|
|
669
|
-
},
|
|
670
|
-
ownership: {
|
|
671
|
-
team: 'tractor-explore',
|
|
672
|
-
slack: '#tractor-explore',
|
|
673
|
-
pagerDuty: 'pd-tractor-explore',
|
|
674
|
-
runbookRef: 'runbooks/wave2/explore.md',
|
|
675
|
-
adrRef: 'docs/super-app-rfc-adr/wave2/reference-topology.md#explore',
|
|
676
|
-
blastRadius: {
|
|
677
|
-
tier: 'tier-1-tractor-discovery',
|
|
678
|
-
references: [
|
|
679
|
-
'docs/super-app-rfc-adr/wave2/blast-radius.md#explore',
|
|
680
|
-
'docs/super-app-rfc-adr/wave2/rollback.md#explore-lkg'
|
|
681
|
-
]
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
id: 'decide',
|
|
687
|
-
directory: 'verticals/decide',
|
|
688
|
-
packageSuffix: 'decide',
|
|
689
|
-
displayName: 'Decide Vertical',
|
|
690
|
-
kind: 'vertical',
|
|
691
|
-
domain: 'decide',
|
|
692
|
-
portEnv: 'VERTICAL_DECIDE_PORT',
|
|
693
|
-
port: 3022,
|
|
694
|
-
mfName: 'verticalDecide',
|
|
695
|
-
verticalRefs: [
|
|
696
|
-
'explore',
|
|
697
|
-
'checkout'
|
|
698
|
-
],
|
|
699
|
-
exposes: {
|
|
700
|
-
'./ProductPage': './src/components/product-page.tsx',
|
|
701
|
-
'./Route': './src/federation-entry.tsx'
|
|
702
|
-
},
|
|
703
|
-
effectApi: {
|
|
704
|
-
stem: 'decide',
|
|
705
|
-
prefix: '/decide-api',
|
|
706
|
-
consumedBy: [
|
|
707
|
-
shellApp.id,
|
|
708
|
-
'decide'
|
|
709
|
-
]
|
|
710
|
-
},
|
|
711
|
-
ownership: {
|
|
712
|
-
team: 'tractor-decide',
|
|
713
|
-
slack: '#tractor-decide',
|
|
714
|
-
pagerDuty: 'pd-tractor-decide',
|
|
715
|
-
runbookRef: 'runbooks/wave2/decide.md',
|
|
716
|
-
adrRef: 'docs/super-app-rfc-adr/wave2/reference-topology.md#decide',
|
|
717
|
-
blastRadius: {
|
|
718
|
-
tier: 'tier-1-tractor-configuration',
|
|
719
|
-
references: [
|
|
720
|
-
'docs/super-app-rfc-adr/wave2/blast-radius.md#decide',
|
|
721
|
-
'docs/super-app-rfc-adr/wave2/rollback.md#decide-lkg'
|
|
722
|
-
]
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
},
|
|
726
|
-
{
|
|
727
|
-
id: 'checkout',
|
|
728
|
-
directory: 'verticals/checkout',
|
|
729
|
-
packageSuffix: 'checkout',
|
|
730
|
-
displayName: 'Checkout Vertical',
|
|
731
|
-
kind: 'vertical',
|
|
732
|
-
domain: 'checkout',
|
|
733
|
-
portEnv: 'VERTICAL_CHECKOUT_PORT',
|
|
734
|
-
port: 3023,
|
|
735
|
-
mfName: 'verticalCheckout',
|
|
736
|
-
exposes: {
|
|
737
|
-
'./AddToCart': './src/components/add-to-cart.tsx',
|
|
738
|
-
'./CartPage': './src/components/cart-page.tsx',
|
|
739
|
-
'./CheckoutPage': './src/components/checkout-page.tsx',
|
|
740
|
-
'./MiniCart': './src/components/mini-cart.tsx',
|
|
741
|
-
'./Route': './src/federation-entry.tsx',
|
|
742
|
-
'./ThanksPage': './src/components/thanks-page.tsx'
|
|
743
|
-
},
|
|
744
|
-
effectApi: {
|
|
745
|
-
stem: 'checkout',
|
|
746
|
-
prefix: '/checkout-api',
|
|
747
|
-
consumedBy: [
|
|
748
|
-
shellApp.id,
|
|
749
|
-
'checkout'
|
|
750
|
-
]
|
|
751
|
-
},
|
|
752
|
-
ownership: {
|
|
753
|
-
team: 'tractor-checkout',
|
|
754
|
-
slack: '#tractor-checkout',
|
|
755
|
-
pagerDuty: 'pd-tractor-checkout',
|
|
756
|
-
runbookRef: 'runbooks/wave2/checkout.md',
|
|
757
|
-
adrRef: 'docs/super-app-rfc-adr/wave2/reference-topology.md#checkout',
|
|
758
|
-
blastRadius: {
|
|
759
|
-
tier: 'tier-1-tractor-purchase',
|
|
760
|
-
references: [
|
|
761
|
-
'docs/super-app-rfc-adr/wave2/blast-radius.md#checkout',
|
|
762
|
-
'docs/super-app-rfc-adr/wave2/rollback.md#checkout-lkg'
|
|
763
|
-
]
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
];
|
|
642
|
+
function createShellHost(remotes = []) {
|
|
643
|
+
return {
|
|
644
|
+
...shellApp,
|
|
645
|
+
verticalRefs: remotes.map((remote)=>remote.id)
|
|
646
|
+
};
|
|
647
|
+
}
|
|
768
648
|
const effectDiagnostics = [
|
|
769
649
|
'anyUnknownInErrorContext',
|
|
770
650
|
'classSelfMismatch',
|
|
@@ -901,20 +781,16 @@ function createVerticalDescriptor(name, port) {
|
|
|
901
781
|
ownership: createNeutralOwnership(id)
|
|
902
782
|
};
|
|
903
783
|
}
|
|
904
|
-
function serviceApiPrefix(service) {
|
|
905
|
-
const name = service.id.replace(/^service-/, '').replace(/-effect$/, '');
|
|
906
|
-
return name.endsWith('-api') ? `/${name}` : `/${name}-api`;
|
|
907
|
-
}
|
|
908
784
|
function appHasEffectApi(app) {
|
|
909
785
|
return void 0 !== app.effectApi;
|
|
910
786
|
}
|
|
911
787
|
function effectApiPrefix(target) {
|
|
912
|
-
return target.effectApi?.prefix ??
|
|
788
|
+
return target.effectApi?.prefix ?? `/${toKebabCase(target.id)}-api`;
|
|
913
789
|
}
|
|
914
790
|
function effectApiStem(target) {
|
|
915
|
-
return target.effectApi?.stem ?? target.id
|
|
791
|
+
return target.effectApi?.stem ?? toKebabCase(target.id).replace(/-api$/, '');
|
|
916
792
|
}
|
|
917
|
-
function verticalEffectApps(remotes =
|
|
793
|
+
function verticalEffectApps(remotes = []) {
|
|
918
794
|
return remotes.filter(appHasEffectApi);
|
|
919
795
|
}
|
|
920
796
|
function normalizePath(filePath) {
|
|
@@ -1032,7 +908,7 @@ function modernPackageSpecifier(packageName, packageSource) {
|
|
|
1032
908
|
if (!packageSource.aliasScope) return packageSource.modernPackageVersion;
|
|
1033
909
|
return `npm:${modernAliasPackageName(packageName, packageSource)}@${packageSource.modernPackageVersion}`;
|
|
1034
910
|
}
|
|
1035
|
-
function appDependencies(scope, packageSource, app) {
|
|
911
|
+
function appDependencies(scope, packageSource, app, remotes = []) {
|
|
1036
912
|
const dependencies = {
|
|
1037
913
|
'@modern-js/plugin-tanstack': modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
|
|
1038
914
|
'@modern-js/plugin-i18n': modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
|
|
@@ -1050,9 +926,9 @@ function appDependencies(scope, packageSource, app) {
|
|
|
1050
926
|
};
|
|
1051
927
|
if ('shell' === app.kind) {
|
|
1052
928
|
dependencies['@modern-js/plugin-bff'] = modernPackageSpecifier('@modern-js/plugin-bff', packageSource);
|
|
1053
|
-
for (const remote of verticalEffectApps())dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
929
|
+
for (const remote of verticalEffectApps(remotes))dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
1054
930
|
}
|
|
1055
|
-
for (const remote of resolveRemoteRefs(app))dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
931
|
+
for (const remote of resolveRemoteRefs(app, remotes))dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
1056
932
|
if (appHasEffectApi(app)) dependencies['@modern-js/plugin-bff'] = modernPackageSpecifier('@modern-js/plugin-bff', packageSource);
|
|
1057
933
|
return dependencies;
|
|
1058
934
|
}
|
|
@@ -1074,7 +950,12 @@ function appDevDependencies(packageSource, enableTailwind) {
|
|
|
1074
950
|
wrangler: WRANGLER_VERSION
|
|
1075
951
|
};
|
|
1076
952
|
}
|
|
1077
|
-
function createRootPackageJson(scope, packageSource) {
|
|
953
|
+
function createRootPackageJson(scope, packageSource, remotes = []) {
|
|
954
|
+
const shellFilter = `--filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)}`;
|
|
955
|
+
const remoteFilters = remotes.map((remote)=>`--filter ${ultramodern_workspace_packageName(scope, remote.packageSuffix)}`);
|
|
956
|
+
const remoteBuildPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run build && ' : '';
|
|
957
|
+
const remoteCloudflareBuildPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && ' : '';
|
|
958
|
+
const remoteCloudflareDeployPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && ' : '';
|
|
1078
959
|
return {
|
|
1079
960
|
private: true,
|
|
1080
961
|
name: scope,
|
|
@@ -1082,19 +963,23 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
1082
963
|
type: 'module',
|
|
1083
964
|
packageManager: `pnpm@${PNPM_VERSION}`,
|
|
1084
965
|
scripts: {
|
|
1085
|
-
dev: `pnpm --parallel
|
|
966
|
+
dev: `pnpm --parallel ${[
|
|
967
|
+
shellFilter,
|
|
968
|
+
...remoteFilters
|
|
969
|
+
].join(' ')} dev`,
|
|
1086
970
|
'dev:shell': `pnpm --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} dev`,
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
971
|
+
...Object.fromEntries(remotes.map((remote)=>[
|
|
972
|
+
`dev:${remote.packageSuffix}`,
|
|
973
|
+
`pnpm --filter ${ultramodern_workspace_packageName(scope, remote.packageSuffix)} dev`
|
|
974
|
+
])),
|
|
975
|
+
build: `${remoteBuildPrefix}pnpm --filter "./apps/shell-super-app" run build && pnpm ultramodern:assert-mf-types`,
|
|
1091
976
|
format: 'oxfmt .',
|
|
1092
977
|
'format:check': 'oxfmt --check .',
|
|
1093
978
|
lint: 'oxlint .',
|
|
1094
979
|
'lint:fix': 'oxlint . --fix',
|
|
1095
980
|
typecheck: `pnpm -r --filter "@${scope}/*" typecheck`,
|
|
1096
|
-
'cloudflare:build':
|
|
1097
|
-
'cloudflare:deploy':
|
|
981
|
+
'cloudflare:build': `${remoteCloudflareBuildPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm ultramodern:assert-mf-types`,
|
|
982
|
+
'cloudflare:deploy': `${remoteCloudflareDeployPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:deploy`,
|
|
1098
983
|
'cloudflare:proof': "node ./scripts/proof-cloudflare-version.mjs --out .codex/reports/cloudflare-version-proof/public-url-proof.json",
|
|
1099
984
|
'skills:install': "node ./scripts/bootstrap-agent-skills.mjs",
|
|
1100
985
|
'skills:check': "node ./scripts/bootstrap-agent-skills.mjs --check",
|
|
@@ -1142,11 +1027,11 @@ function remoteDependencyAlias(remote) {
|
|
|
1142
1027
|
function zephyrRemoteDependency(scope, remote) {
|
|
1143
1028
|
return `${ultramodern_workspace_packageName(scope, remote.packageSuffix)}@workspace:*`;
|
|
1144
1029
|
}
|
|
1145
|
-
function resolveRemoteRefs(app, remotes =
|
|
1030
|
+
function resolveRemoteRefs(app, remotes = []) {
|
|
1146
1031
|
const verticalRefs = app.verticalRefs ?? [];
|
|
1147
1032
|
return verticalRefs.map((remoteRef)=>remotes.find((remote)=>remote.id === remoteRef)).filter((remote)=>void 0 !== remote);
|
|
1148
1033
|
}
|
|
1149
|
-
function createModuleFederationRemoteContracts(app, remotes =
|
|
1034
|
+
function createModuleFederationRemoteContracts(app, remotes = []) {
|
|
1150
1035
|
return resolveRemoteRefs(app, remotes).map((remote)=>({
|
|
1151
1036
|
id: remote.id,
|
|
1152
1037
|
alias: remoteDependencyAlias(remote),
|
|
@@ -1155,7 +1040,7 @@ function createModuleFederationRemoteContracts(app, remotes = verticalApps) {
|
|
|
1155
1040
|
manifestUrl: `http://localhost:${remote.port}/mf-manifest.json`
|
|
1156
1041
|
}));
|
|
1157
1042
|
}
|
|
1158
|
-
function createZephyrDependencies(scope, app, remotes =
|
|
1043
|
+
function createZephyrDependencies(scope, app, remotes = []) {
|
|
1159
1044
|
if (!app.verticalRefs?.length) return {};
|
|
1160
1045
|
return Object.fromEntries(resolveRemoteRefs(app, remotes).map((remote)=>[
|
|
1161
1046
|
remoteDependencyAlias(remote),
|
|
@@ -1262,7 +1147,7 @@ function createPackageTsConfig(packageDir, includeApi = false) {
|
|
|
1262
1147
|
]
|
|
1263
1148
|
};
|
|
1264
1149
|
}
|
|
1265
|
-
function createAppPackage(scope, app, packageSource, enableTailwind) {
|
|
1150
|
+
function createAppPackage(scope, app, packageSource, enableTailwind, remotes = []) {
|
|
1266
1151
|
const packageExports = Object.fromEntries(Object.entries(app.exposes ?? {}).map(([expose, source])=>[
|
|
1267
1152
|
expose,
|
|
1268
1153
|
source
|
|
@@ -1275,7 +1160,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind) {
|
|
|
1275
1160
|
dev: 'modern dev',
|
|
1276
1161
|
build: app.exposes ? `modern build && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mjs` : 'modern build',
|
|
1277
1162
|
'cloudflare:build': 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy',
|
|
1278
|
-
'cloudflare:deploy': 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true
|
|
1163
|
+
'cloudflare:deploy': 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy',
|
|
1279
1164
|
'cloudflare:preview': 'pnpm run cloudflare:build && wrangler dev --config .output/wrangler.json',
|
|
1280
1165
|
'cloudflare:proof': `node ${relativeRootFor(app.directory)}/scripts/proof-cloudflare-version.mjs --app ${app.id}`,
|
|
1281
1166
|
serve: 'modern serve',
|
|
@@ -1290,8 +1175,8 @@ function createAppPackage(scope, app, packageSource, enableTailwind) {
|
|
|
1290
1175
|
apiRuntime: 'effect-bff'
|
|
1291
1176
|
} : {}
|
|
1292
1177
|
},
|
|
1293
|
-
'zephyr:dependencies': createZephyrDependencies(scope, app),
|
|
1294
|
-
dependencies: appDependencies(scope, packageSource, app),
|
|
1178
|
+
'zephyr:dependencies': createZephyrDependencies(scope, app, remotes),
|
|
1179
|
+
dependencies: appDependencies(scope, packageSource, app, remotes),
|
|
1295
1180
|
devDependencies: appDevDependencies(packageSource, enableTailwind)
|
|
1296
1181
|
};
|
|
1297
1182
|
if (appHasEffectApi(app)) Object.assign(packageExports, {
|
|
@@ -1299,7 +1184,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind) {
|
|
|
1299
1184
|
'./shared/effect/api': './shared/effect/api.ts'
|
|
1300
1185
|
});
|
|
1301
1186
|
else if ('shell' === app.kind) Object.assign(packageExports, {
|
|
1302
|
-
'./effect/clients': './src/effect/
|
|
1187
|
+
'./effect/clients': './src/effect/vertical-clients.ts'
|
|
1303
1188
|
});
|
|
1304
1189
|
if (Object.keys(packageExports).length > 0) packageJson.exports = packageExports;
|
|
1305
1190
|
return packageJson;
|
|
@@ -1563,7 +1448,7 @@ ${entries.map(([key, entryValue])=>` '${key}': '${entryValue}',`).join('\n')}
|
|
|
1563
1448
|
function createRemoteManifestEnv(remote) {
|
|
1564
1449
|
return `VERTICAL_${toEnvSegment(remote.domain ?? remote.id)}_MF_MANIFEST`;
|
|
1565
1450
|
}
|
|
1566
|
-
function createModuleFederationRemoteUrlHelpers(app, remotes =
|
|
1451
|
+
function createModuleFederationRemoteUrlHelpers(app, remotes = []) {
|
|
1567
1452
|
if (0 === resolveRemoteRefs(app, remotes).length) return '';
|
|
1568
1453
|
return `const cloudflareDeployEnabled =
|
|
1569
1454
|
process.env['MODERNJS_DEPLOY'] === 'cloudflare';
|
|
@@ -1604,7 +1489,7 @@ function createRemoteManifestUrl(options: {
|
|
|
1604
1489
|
|
|
1605
1490
|
`;
|
|
1606
1491
|
}
|
|
1607
|
-
function createModuleFederationRemotesConfig(scope, app, remotes =
|
|
1492
|
+
function createModuleFederationRemotesConfig(scope, app, remotes = []) {
|
|
1608
1493
|
const remoteEntries = resolveRemoteRefs(app, remotes).map((remote)=>{
|
|
1609
1494
|
const key = remoteDependencyAlias(remote);
|
|
1610
1495
|
return ` ${key}: createRemoteManifestUrl({
|
|
@@ -1621,7 +1506,7 @@ ${remoteEntries}
|
|
|
1621
1506
|
},
|
|
1622
1507
|
`;
|
|
1623
1508
|
}
|
|
1624
|
-
function createShellModuleFederationConfig(scope, remotes =
|
|
1509
|
+
function createShellModuleFederationConfig(scope, remotes = []) {
|
|
1625
1510
|
const shellHost = {
|
|
1626
1511
|
...shellApp,
|
|
1627
1512
|
verticalRefs: remotes.map((remote)=>remote.id)
|
|
@@ -1679,7 +1564,7 @@ export const ultramodernApiMarker = {
|
|
|
1679
1564
|
} as const;
|
|
1680
1565
|
`;
|
|
1681
1566
|
}
|
|
1682
|
-
function createRemoteModuleFederationConfig(scope, app, remotes =
|
|
1567
|
+
function createRemoteModuleFederationConfig(scope, app, remotes = []) {
|
|
1683
1568
|
const exposes = formatTsObjectLiteral(app.exposes ?? {});
|
|
1684
1569
|
return `// @effect-diagnostics nodeBuiltinImport:off
|
|
1685
1570
|
import { createRequire } from 'node:module';
|
|
@@ -1732,192 +1617,152 @@ function createRouteOwnedI18nPaths(app) {
|
|
|
1732
1617
|
en: '/'
|
|
1733
1618
|
},
|
|
1734
1619
|
titleKey: 'shell.title'
|
|
1735
|
-
},
|
|
1736
|
-
{
|
|
1737
|
-
...base,
|
|
1738
|
-
canonicalPath: '/tractors',
|
|
1739
|
-
id: 'shell-tractors',
|
|
1740
|
-
localisedPaths: {
|
|
1741
|
-
cs: '/traktory',
|
|
1742
|
-
en: '/tractors'
|
|
1743
|
-
},
|
|
1744
|
-
titleKey: 'shell.routes.listing'
|
|
1745
|
-
},
|
|
1746
|
-
{
|
|
1747
|
-
...base,
|
|
1748
|
-
canonicalPath: '/stores',
|
|
1749
|
-
id: 'shell-stores',
|
|
1750
|
-
localisedPaths: {
|
|
1751
|
-
cs: '/prodejci',
|
|
1752
|
-
en: '/stores'
|
|
1753
|
-
},
|
|
1754
|
-
titleKey: 'shell.routes.storePicker'
|
|
1755
|
-
},
|
|
1756
|
-
{
|
|
1757
|
-
...base,
|
|
1758
|
-
canonicalPath: '/tractors/:slug',
|
|
1759
|
-
id: 'shell-product-detail',
|
|
1760
|
-
localisedPaths: {
|
|
1761
|
-
cs: '/traktory/:slug',
|
|
1762
|
-
en: '/tractors/:slug'
|
|
1763
|
-
},
|
|
1764
|
-
titleKey: 'shell.routes.productDetail'
|
|
1765
|
-
},
|
|
1766
|
-
{
|
|
1767
|
-
...base,
|
|
1768
|
-
canonicalPath: '/cart',
|
|
1769
|
-
id: 'shell-cart',
|
|
1770
|
-
localisedPaths: {
|
|
1771
|
-
cs: '/kosik',
|
|
1772
|
-
en: '/cart'
|
|
1773
|
-
},
|
|
1774
|
-
titleKey: 'shell.routes.cart'
|
|
1775
1620
|
}
|
|
1776
1621
|
];
|
|
1777
|
-
if ('
|
|
1622
|
+
if ('workspace' === app.domain) return [
|
|
1778
1623
|
{
|
|
1779
1624
|
...base,
|
|
1780
1625
|
canonicalPath: '/',
|
|
1781
|
-
id: '
|
|
1626
|
+
id: 'workspace-home',
|
|
1782
1627
|
localisedPaths: {
|
|
1783
1628
|
cs: '/',
|
|
1784
1629
|
en: '/'
|
|
1785
1630
|
},
|
|
1786
|
-
titleKey: '
|
|
1631
|
+
titleKey: 'workspace.title'
|
|
1787
1632
|
},
|
|
1788
1633
|
{
|
|
1789
1634
|
...base,
|
|
1790
|
-
canonicalPath: '/
|
|
1791
|
-
id: '
|
|
1635
|
+
canonicalPath: '/workspaces',
|
|
1636
|
+
id: 'workspace-listing',
|
|
1792
1637
|
localisedPaths: {
|
|
1793
|
-
cs: '/
|
|
1794
|
-
en: '/
|
|
1638
|
+
cs: '/pracovni-prostory',
|
|
1639
|
+
en: '/workspaces'
|
|
1795
1640
|
},
|
|
1796
|
-
titleKey: '
|
|
1641
|
+
titleKey: 'workspace.routes.workspaces'
|
|
1797
1642
|
},
|
|
1798
1643
|
{
|
|
1799
1644
|
...base,
|
|
1800
|
-
canonicalPath: '/
|
|
1801
|
-
id: '
|
|
1645
|
+
canonicalPath: '/directory',
|
|
1646
|
+
id: 'workspace-directory',
|
|
1802
1647
|
localisedPaths: {
|
|
1803
|
-
cs: '/
|
|
1804
|
-
en: '/
|
|
1648
|
+
cs: '/adresar',
|
|
1649
|
+
en: '/directory'
|
|
1805
1650
|
},
|
|
1806
|
-
titleKey: '
|
|
1651
|
+
titleKey: 'workspace.routes.directory'
|
|
1807
1652
|
},
|
|
1808
1653
|
{
|
|
1809
1654
|
...base,
|
|
1810
1655
|
canonicalPath: '/unavailable',
|
|
1811
|
-
id: '
|
|
1656
|
+
id: 'workspace-unavailable',
|
|
1812
1657
|
localisedPaths: {
|
|
1813
1658
|
cs: '/nedostupne',
|
|
1814
1659
|
en: '/unavailable'
|
|
1815
1660
|
},
|
|
1816
|
-
titleKey: '
|
|
1661
|
+
titleKey: 'workspace.routes.unavailable'
|
|
1817
1662
|
}
|
|
1818
1663
|
];
|
|
1819
|
-
if ('
|
|
1664
|
+
if ('records' === app.domain) return [
|
|
1820
1665
|
{
|
|
1821
1666
|
...base,
|
|
1822
1667
|
canonicalPath: '/',
|
|
1823
|
-
id: '
|
|
1668
|
+
id: 'records-home',
|
|
1824
1669
|
localisedPaths: {
|
|
1825
1670
|
cs: '/',
|
|
1826
1671
|
en: '/'
|
|
1827
1672
|
},
|
|
1828
|
-
titleKey: '
|
|
1673
|
+
titleKey: 'records.title'
|
|
1829
1674
|
},
|
|
1830
1675
|
{
|
|
1831
1676
|
...base,
|
|
1832
|
-
canonicalPath: '/
|
|
1833
|
-
id: '
|
|
1677
|
+
canonicalPath: '/workspaces',
|
|
1678
|
+
id: 'records-workspace-parent',
|
|
1834
1679
|
localisedPaths: {
|
|
1835
|
-
cs: '/
|
|
1836
|
-
en: '/
|
|
1680
|
+
cs: '/pracovni-prostory',
|
|
1681
|
+
en: '/workspaces'
|
|
1837
1682
|
},
|
|
1838
|
-
titleKey: '
|
|
1683
|
+
titleKey: 'records.routes.workspaces'
|
|
1839
1684
|
},
|
|
1840
1685
|
{
|
|
1841
1686
|
...base,
|
|
1842
|
-
canonicalPath: '/
|
|
1843
|
-
id: '
|
|
1687
|
+
canonicalPath: '/records/:slug',
|
|
1688
|
+
id: 'records-detail',
|
|
1844
1689
|
localisedPaths: {
|
|
1845
|
-
cs: '/
|
|
1846
|
-
en: '/
|
|
1690
|
+
cs: '/zaznamy/:slug',
|
|
1691
|
+
en: '/records/:slug'
|
|
1847
1692
|
},
|
|
1848
|
-
titleKey: '
|
|
1693
|
+
titleKey: 'records.routes.recordDetail'
|
|
1849
1694
|
},
|
|
1850
1695
|
{
|
|
1851
1696
|
...base,
|
|
1852
1697
|
canonicalPath: '/unavailable',
|
|
1853
|
-
id: '
|
|
1698
|
+
id: 'records-unavailable',
|
|
1854
1699
|
localisedPaths: {
|
|
1855
1700
|
cs: '/nedostupne',
|
|
1856
1701
|
en: '/unavailable'
|
|
1857
1702
|
},
|
|
1858
|
-
titleKey: '
|
|
1703
|
+
titleKey: 'records.routes.unavailable'
|
|
1859
1704
|
}
|
|
1860
1705
|
];
|
|
1861
|
-
if ('
|
|
1706
|
+
if ('actions' === app.domain) return [
|
|
1862
1707
|
{
|
|
1863
1708
|
...base,
|
|
1864
1709
|
canonicalPath: '/',
|
|
1865
|
-
id: '
|
|
1710
|
+
id: 'actions-home',
|
|
1866
1711
|
localisedPaths: {
|
|
1867
1712
|
cs: '/',
|
|
1868
1713
|
en: '/'
|
|
1869
1714
|
},
|
|
1870
|
-
titleKey: '
|
|
1715
|
+
titleKey: 'actions.title'
|
|
1871
1716
|
},
|
|
1872
1717
|
{
|
|
1873
1718
|
...base,
|
|
1874
|
-
canonicalPath: '/
|
|
1875
|
-
id: '
|
|
1719
|
+
canonicalPath: '/actions',
|
|
1720
|
+
id: 'actions-queue',
|
|
1876
1721
|
localisedPaths: {
|
|
1877
|
-
cs: '/
|
|
1878
|
-
en: '/
|
|
1722
|
+
cs: '/akce',
|
|
1723
|
+
en: '/actions'
|
|
1879
1724
|
},
|
|
1880
|
-
titleKey: '
|
|
1725
|
+
titleKey: 'actions.routes.actions'
|
|
1881
1726
|
},
|
|
1882
1727
|
{
|
|
1883
1728
|
...base,
|
|
1884
|
-
canonicalPath: '/
|
|
1885
|
-
id: '
|
|
1729
|
+
canonicalPath: '/actions/review',
|
|
1730
|
+
id: 'actions-review',
|
|
1886
1731
|
localisedPaths: {
|
|
1887
|
-
cs: '/
|
|
1888
|
-
en: '/
|
|
1732
|
+
cs: '/akce/revize',
|
|
1733
|
+
en: '/actions/review'
|
|
1889
1734
|
},
|
|
1890
|
-
titleKey: '
|
|
1735
|
+
titleKey: 'actions.routes.review'
|
|
1891
1736
|
},
|
|
1892
1737
|
{
|
|
1893
1738
|
...base,
|
|
1894
|
-
canonicalPath: '/
|
|
1895
|
-
id: '
|
|
1739
|
+
canonicalPath: '/actions/done',
|
|
1740
|
+
id: 'actions-done-parent',
|
|
1896
1741
|
localisedPaths: {
|
|
1897
|
-
cs: '/
|
|
1898
|
-
en: '/
|
|
1742
|
+
cs: '/akce/hotovo',
|
|
1743
|
+
en: '/actions/done'
|
|
1899
1744
|
},
|
|
1900
|
-
titleKey: '
|
|
1745
|
+
titleKey: 'actions.routes.done'
|
|
1901
1746
|
},
|
|
1902
1747
|
{
|
|
1903
1748
|
...base,
|
|
1904
|
-
canonicalPath: '/
|
|
1905
|
-
id: '
|
|
1749
|
+
canonicalPath: '/actions/done/:actionId?',
|
|
1750
|
+
id: 'actions-done',
|
|
1906
1751
|
localisedPaths: {
|
|
1907
|
-
cs: '/
|
|
1908
|
-
en: '/
|
|
1752
|
+
cs: '/akce/hotovo/:actionId?',
|
|
1753
|
+
en: '/actions/done/:actionId?'
|
|
1909
1754
|
},
|
|
1910
|
-
titleKey: '
|
|
1755
|
+
titleKey: 'actions.routes.done'
|
|
1911
1756
|
},
|
|
1912
1757
|
{
|
|
1913
1758
|
...base,
|
|
1914
1759
|
canonicalPath: '/unavailable',
|
|
1915
|
-
id: '
|
|
1760
|
+
id: 'actions-unavailable',
|
|
1916
1761
|
localisedPaths: {
|
|
1917
1762
|
cs: '/nedostupne',
|
|
1918
1763
|
en: '/unavailable'
|
|
1919
1764
|
},
|
|
1920
|
-
titleKey: '
|
|
1765
|
+
titleKey: 'actions.routes.unavailable'
|
|
1921
1766
|
}
|
|
1922
1767
|
];
|
|
1923
1768
|
return [
|
|
@@ -1983,7 +1828,24 @@ function createRouteAliasPage(canonicalPath) {
|
|
|
1983
1828
|
return `export { default } from '${rootPageImport}';
|
|
1984
1829
|
`;
|
|
1985
1830
|
}
|
|
1986
|
-
function
|
|
1831
|
+
function createBoundaryDebugMetadata(scope, remotes = []) {
|
|
1832
|
+
return {
|
|
1833
|
+
schemaVersion: 1,
|
|
1834
|
+
appId: shellApp.id,
|
|
1835
|
+
boundaries: [
|
|
1836
|
+
shellApp,
|
|
1837
|
+
...remotes
|
|
1838
|
+
].map((app)=>({
|
|
1839
|
+
appId: app.id,
|
|
1840
|
+
mfName: app.mfName,
|
|
1841
|
+
packageName: ultramodern_workspace_packageName(scope, app.packageSuffix),
|
|
1842
|
+
role: 'shell' === app.kind ? 'host' : 'vertical',
|
|
1843
|
+
ownerTeam: app.ownership.team,
|
|
1844
|
+
label: app.displayName
|
|
1845
|
+
}))
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
function createAppEnvDts(app, remotes = []) {
|
|
1987
1849
|
const remoteModuleDeclarations = resolveRemoteRefs(app, remotes).flatMap((remote)=>Object.keys(remote.exposes ?? {}).filter((expose)=>'./Route' !== expose).map((expose)=>{
|
|
1988
1850
|
const moduleName = `${remoteDependencyAlias(remote)}/${expose.replace(/^\.\//u, '')}`;
|
|
1989
1851
|
return `declare module '${moduleName}' {
|
|
@@ -2001,24 +1863,15 @@ declare module '*.svg' {
|
|
|
2001
1863
|
}
|
|
2002
1864
|
${remoteModuleDeclarations ? `\n${remoteModuleDeclarations}` : ''}`;
|
|
2003
1865
|
}
|
|
2004
|
-
function createAppRuntimeConfig(app) {
|
|
2005
|
-
const
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
cs: {
|
|
2012
|
-
[namespace]: localeMessages('cs'),
|
|
2013
|
-
translation: localeMessages('cs')
|
|
2014
|
-
},
|
|
2015
|
-
en: {
|
|
2016
|
-
[namespace]: localeMessages('en'),
|
|
2017
|
-
translation: localeMessages('en')
|
|
2018
|
-
}
|
|
2019
|
-
};
|
|
1866
|
+
function createAppRuntimeConfig(app, scope, remotes = []) {
|
|
1867
|
+
const pluginsConfig = 'shell' === app.kind ? ` plugins: [
|
|
1868
|
+
ultramodernBoundaryDebuggerPlugin({
|
|
1869
|
+
metadata: ${JSON.stringify(createBoundaryDebugMetadata(scope, remotes), null, 6).split('\n').join('\n ')},
|
|
1870
|
+
}),
|
|
1871
|
+
],
|
|
1872
|
+
` : '';
|
|
2020
1873
|
return `import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
2021
|
-
import { createInstance } from 'i18next';
|
|
1874
|
+
${'shell' === app.kind ? "import { ultramodernBoundaryDebuggerPlugin } from '@modern-js/runtime/boundary-debugger';\n" : ''}import { createInstance } from 'i18next';
|
|
2022
1875
|
import { ultramodernRouteNamespace } from './routes/ultramodern-route-metadata';
|
|
2023
1876
|
|
|
2024
1877
|
const i18nInstance = createInstance();
|
|
@@ -2033,13 +1886,13 @@ export default defineRuntimeConfig({
|
|
|
2033
1886
|
escapeValue: false,
|
|
2034
1887
|
},
|
|
2035
1888
|
ns: [ultramodernRouteNamespace, 'translation'],
|
|
2036
|
-
resources: ${JSON.stringify(resources, null, 8).split('\n').join('\n ')},
|
|
2037
1889
|
supportedLngs: ['en', 'cs'],
|
|
2038
1890
|
},
|
|
2039
1891
|
},
|
|
2040
1892
|
router: {
|
|
2041
1893
|
framework: 'tanstack',
|
|
2042
1894
|
},
|
|
1895
|
+
${pluginsConfig}
|
|
2043
1896
|
});
|
|
2044
1897
|
`;
|
|
2045
1898
|
}
|
|
@@ -2055,21 +1908,12 @@ function tailwindPrefixForApp(app) {
|
|
|
2055
1908
|
if ('shell' === app.kind) return 'shell';
|
|
2056
1909
|
return createTailwindPrefix(app.domain ?? app.id);
|
|
2057
1910
|
}
|
|
2058
|
-
function
|
|
2059
|
-
return createTailwindPrefix(service.id);
|
|
2060
|
-
}
|
|
2061
|
-
function assertUniqueTailwindPrefixes(apps, services = []) {
|
|
1911
|
+
function assertUniqueTailwindPrefixes(apps) {
|
|
2062
1912
|
const seen = new Map();
|
|
2063
|
-
const entries = [
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
]),
|
|
2068
|
-
...services.map((service)=>[
|
|
2069
|
-
service.id,
|
|
2070
|
-
tailwindPrefixForService(service)
|
|
2071
|
-
])
|
|
2072
|
-
];
|
|
1913
|
+
const entries = apps.map((app)=>[
|
|
1914
|
+
app.id,
|
|
1915
|
+
tailwindPrefixForApp(app)
|
|
1916
|
+
]);
|
|
2073
1917
|
for (const [id, prefix] of entries){
|
|
2074
1918
|
const previous = seen.get(prefix);
|
|
2075
1919
|
if (previous) throw new Error(`Tailwind prefix ${prefix} for ${id} collides with ${previous}`);
|
|
@@ -2106,115 +1950,7 @@ export default {
|
|
|
2106
1950
|
function createTw(prefix) {
|
|
2107
1951
|
return (classList)=>classList.split(/\s+/u).filter(Boolean).map((candidate)=>`${prefix}:${candidate.replace(/\[&&\]:/gu, '')}`).join(' ');
|
|
2108
1952
|
}
|
|
2109
|
-
function
|
|
2110
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="900" viewBox="0 0 1440 900" role="img" aria-label="${title}">
|
|
2111
|
-
<defs>
|
|
2112
|
-
<linearGradient id="sky" x1="0" x2="0" y1="0" y2="1">
|
|
2113
|
-
<stop offset="0" stop-color="${palette.sky}"/>
|
|
2114
|
-
<stop offset="1" stop-color="#fff8dc"/>
|
|
2115
|
-
</linearGradient>
|
|
2116
|
-
<linearGradient id="field" x1="0" x2="1" y1="0" y2="1">
|
|
2117
|
-
<stop offset="0" stop-color="${palette.ground}"/>
|
|
2118
|
-
<stop offset="1" stop-color="${palette.accent}"/>
|
|
2119
|
-
</linearGradient>
|
|
2120
|
-
</defs>
|
|
2121
|
-
<rect width="1440" height="900" fill="url(#sky)"/>
|
|
2122
|
-
<path d="M0 566c172-78 330-102 474-72 125 26 219 91 340 106 170 21 339-74 626-43v343H0z" fill="url(#field)"/>
|
|
2123
|
-
<path d="M0 686c205-70 451-66 738 12 287 77 521 66 702-33v235H0z" fill="#4f7f38" opacity=".55"/>
|
|
2124
|
-
<g fill="none" stroke="#fff6b7" stroke-linecap="round" stroke-width="10" opacity=".55">
|
|
2125
|
-
<path d="M108 820c205-138 382-202 530-192"/>
|
|
2126
|
-
<path d="M322 862c176-134 338-198 486-193"/>
|
|
2127
|
-
<path d="M583 886c119-121 260-190 422-207"/>
|
|
2128
|
-
<path d="M868 878c95-94 207-153 336-176"/>
|
|
2129
|
-
</g>
|
|
2130
|
-
<g transform="translate(430 430)">
|
|
2131
|
-
<circle cx="170" cy="210" r="92" fill="#161616"/>
|
|
2132
|
-
<circle cx="170" cy="210" r="54" fill="#d7c46d"/>
|
|
2133
|
-
<circle cx="514" cy="214" r="108" fill="#161616"/>
|
|
2134
|
-
<circle cx="514" cy="214" r="63" fill="#d7c46d"/>
|
|
2135
|
-
<path d="M194 142h194l72-100h114c49 0 89 39 89 88v57H625l-51-90H452l-78 114H209z" fill="${palette.tractor}"/>
|
|
2136
|
-
<path d="M283 67h134l-54 73H249z" fill="#c9ecff" opacity=".72"/>
|
|
2137
|
-
<path d="M120 184h430v54H120z" fill="${palette.tractor}"/>
|
|
2138
|
-
<path d="M578 52l60-35M618 37l34 72" stroke="#171717" stroke-linecap="round" stroke-width="14"/>
|
|
2139
|
-
<path d="M90 236h578" stroke="#171717" stroke-linecap="round" stroke-width="18"/>
|
|
2140
|
-
</g>
|
|
2141
|
-
</svg>
|
|
2142
|
-
`;
|
|
2143
|
-
}
|
|
2144
|
-
const commerceAssetPublicRoot = 'assets/ultramodern';
|
|
2145
|
-
function commerceAssetPublicPath(filename) {
|
|
2146
|
-
return `${commerceAssetPublicRoot}/${filename}`;
|
|
2147
|
-
}
|
|
2148
|
-
function commerceAssetUrl(filename) {
|
|
2149
|
-
return `/${commerceAssetPublicRoot}/${filename}`;
|
|
2150
|
-
}
|
|
2151
|
-
function commerceAssetsForApp(app) {
|
|
2152
|
-
if ('shell' === app.kind) return {
|
|
2153
|
-
[commerceAssetPublicPath('hero-field.svg')]: createCommerceAssetSvg('Tractor crossing cultivated fields', {
|
|
2154
|
-
accent: '#d6b85d',
|
|
2155
|
-
ground: '#84ad58',
|
|
2156
|
-
sky: '#9fd6e8',
|
|
2157
|
-
tractor: '#005f73'
|
|
2158
|
-
}),
|
|
2159
|
-
[commerceAssetPublicPath('autonomy.svg')]: createCommerceAssetSvg('Autonomous tractor concept', {
|
|
2160
|
-
accent: '#c26a2e',
|
|
2161
|
-
ground: '#668f55',
|
|
2162
|
-
sky: '#d5e7de',
|
|
2163
|
-
tractor: '#f2a51a'
|
|
2164
|
-
}),
|
|
2165
|
-
[commerceAssetPublicPath('field-loader.svg')]: createCommerceAssetSvg('Field Loader 112 tractor', {
|
|
2166
|
-
accent: '#d6b85d',
|
|
2167
|
-
ground: '#84ad58',
|
|
2168
|
-
sky: '#9fd6e8',
|
|
2169
|
-
tractor: '#00624b'
|
|
2170
|
-
}),
|
|
2171
|
-
[commerceAssetPublicPath('orchard.svg')]: createCommerceAssetSvg('Orchard tractor between tight rows', {
|
|
2172
|
-
accent: '#b45b2d',
|
|
2173
|
-
ground: '#6f9b4a',
|
|
2174
|
-
sky: '#c9ebff',
|
|
2175
|
-
tractor: '#1d5d9b'
|
|
2176
|
-
}),
|
|
2177
|
-
[commerceAssetPublicPath('vineyard.svg')]: createCommerceAssetSvg('Vineyard narrow tractor', {
|
|
2178
|
-
accent: '#b88d58',
|
|
2179
|
-
ground: '#5e8a45',
|
|
2180
|
-
sky: '#f1dcb9',
|
|
2181
|
-
tractor: '#914d76'
|
|
2182
|
-
})
|
|
2183
|
-
};
|
|
2184
|
-
if ('explore' === app.id) return {
|
|
2185
|
-
[commerceAssetPublicPath('autonomy.svg')]: createCommerceAssetSvg('Autonomous tractor concept', {
|
|
2186
|
-
accent: '#c26a2e',
|
|
2187
|
-
ground: '#668f55',
|
|
2188
|
-
sky: '#d5e7de',
|
|
2189
|
-
tractor: '#f2a51a'
|
|
2190
|
-
}),
|
|
2191
|
-
[commerceAssetPublicPath('field-loader.svg')]: createCommerceAssetSvg('Field Loader 112 tractor', {
|
|
2192
|
-
accent: '#d6b85d',
|
|
2193
|
-
ground: '#84ad58',
|
|
2194
|
-
sky: '#9fd6e8',
|
|
2195
|
-
tractor: '#00624b'
|
|
2196
|
-
}),
|
|
2197
|
-
[commerceAssetPublicPath('orchard.svg')]: createCommerceAssetSvg('Orchard tractor between tight rows', {
|
|
2198
|
-
accent: '#b45b2d',
|
|
2199
|
-
ground: '#6f9b4a',
|
|
2200
|
-
sky: '#c9ebff',
|
|
2201
|
-
tractor: '#1d5d9b'
|
|
2202
|
-
}),
|
|
2203
|
-
[commerceAssetPublicPath('vineyard.svg')]: createCommerceAssetSvg('Vineyard narrow tractor', {
|
|
2204
|
-
accent: '#b88d58',
|
|
2205
|
-
ground: '#5e8a45',
|
|
2206
|
-
sky: '#f1dcb9',
|
|
2207
|
-
tractor: '#914d76'
|
|
2208
|
-
})
|
|
2209
|
-
};
|
|
2210
|
-
if ('decide' === app.id) return {
|
|
2211
|
-
[commerceAssetPublicPath('field-loader.svg')]: createCommerceAssetSvg('Field Loader 112 tractor detail', {
|
|
2212
|
-
accent: '#d6b85d',
|
|
2213
|
-
ground: '#84ad58',
|
|
2214
|
-
sky: '#9fd6e8',
|
|
2215
|
-
tractor: '#00624b'
|
|
2216
|
-
})
|
|
2217
|
-
};
|
|
1953
|
+
function workspaceAssetsForApp(_app) {
|
|
2218
1954
|
return {};
|
|
2219
1955
|
}
|
|
2220
1956
|
function createLocalizedHeadComponent() {
|
|
@@ -2367,21 +2103,20 @@ const LocalizedHead = () => {
|
|
|
2367
2103
|
};
|
|
2368
2104
|
`;
|
|
2369
2105
|
}
|
|
2370
|
-
function createShellPage() {
|
|
2106
|
+
function createShellPage(remotes = []) {
|
|
2371
2107
|
const tw = createTw(tailwindPrefixForApp(shellApp));
|
|
2372
|
-
|
|
2108
|
+
const remoteCount = String(remotes.length);
|
|
2109
|
+
return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2373
2110
|
import { Helmet } from '@modern-js/runtime/head';
|
|
2374
2111
|
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2375
2112
|
import ShellFrame from '../shell-frame';
|
|
2376
|
-
import {
|
|
2113
|
+
import { VerticalShowcase } from '../vertical-components';
|
|
2377
2114
|
import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
|
|
2378
2115
|
import { ultramodernUiMarker } from '../../ultramodern-build';
|
|
2379
2116
|
|
|
2380
|
-
const heroField = '${commerceAssetUrl('hero-field.svg')}';
|
|
2381
|
-
|
|
2382
2117
|
${createLocalizedHeadComponent()}
|
|
2383
2118
|
export default function ShellHome() {
|
|
2384
|
-
const { i18nInstance
|
|
2119
|
+
const { i18nInstance } = useModernI18n();
|
|
2385
2120
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2386
2121
|
|
|
2387
2122
|
return (
|
|
@@ -2393,17 +2128,30 @@ export default function ShellHome() {
|
|
|
2393
2128
|
<h1 className="${tw('mt-3 max-w-3xl text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl')}">{t('shell.title')}</h1>
|
|
2394
2129
|
<p className="${tw('mt-5 max-w-2xl text-lg leading-8 text-stone-600')}">{t('shell.hero.lede')}</p>
|
|
2395
2130
|
<div className="${tw('mt-7 flex flex-wrap gap-3')}">
|
|
2396
|
-
<
|
|
2397
|
-
|
|
2398
|
-
</
|
|
2399
|
-
<
|
|
2400
|
-
|
|
2401
|
-
</
|
|
2131
|
+
<I18nLink className="${tw('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')}" to="/">
|
|
2132
|
+
{t('shell.hero.primary')}
|
|
2133
|
+
</I18nLink>
|
|
2134
|
+
<span className="${tw('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')}">
|
|
2135
|
+
{t('shell.hero.secondary')}
|
|
2136
|
+
</span>
|
|
2137
|
+
</div>
|
|
2138
|
+
</div>
|
|
2139
|
+
<div className="${tw('rounded-3xl bg-white/90 p-6 shadow-2xl shadow-stone-900/15')}">
|
|
2140
|
+
<div className="${tw('grid gap-4 sm:grid-cols-2')}">
|
|
2141
|
+
<article className="${tw('rounded-2xl bg-emerald-50 p-5')}">
|
|
2142
|
+
<span className="${tw('text-sm font-black uppercase tracking-[0.16em] text-emerald-800')}">{t('shell.hero.cardOneKicker')}</span>
|
|
2143
|
+
<strong className="${tw('mt-3 block text-3xl font-black text-stone-950')}">${remoteCount}</strong>
|
|
2144
|
+
<p className="${tw('mt-2 text-sm font-semibold text-stone-600')}">{t('shell.hero.cardOne')}</p>
|
|
2145
|
+
</article>
|
|
2146
|
+
<article className="${tw('rounded-2xl bg-amber-50 p-5')}">
|
|
2147
|
+
<span className="${tw('text-sm font-black uppercase tracking-[0.16em] text-amber-800')}">{t('shell.hero.cardTwoKicker')}</span>
|
|
2148
|
+
<strong className="${tw('mt-3 block text-3xl font-black text-stone-950')}">SSR</strong>
|
|
2149
|
+
<p className="${tw('mt-2 text-sm font-semibold text-stone-600')}">{t('shell.hero.cardTwo')}</p>
|
|
2150
|
+
</article>
|
|
2402
2151
|
</div>
|
|
2403
2152
|
</div>
|
|
2404
|
-
<img alt="" className="${tw('aspect-[16/10] w-full rounded-3xl bg-stone-200 object-cover shadow-2xl shadow-stone-900/20')}" src={heroField} />
|
|
2405
2153
|
</section>
|
|
2406
|
-
<
|
|
2154
|
+
<VerticalShowcase />
|
|
2407
2155
|
<p className="${tw('sr-only')}" data-testid="ultramodern-preset">presetUltramodern workspace</p>
|
|
2408
2156
|
<p className="${tw('sr-only')}" data-build-marker={ultramodernUiMarker.build} data-testid="ultramodern-ui-marker">
|
|
2409
2157
|
{ultramodernUiMarker.appId}:{ultramodernUiMarker.version}
|
|
@@ -2413,94 +2161,19 @@ export default function ShellHome() {
|
|
|
2413
2161
|
}
|
|
2414
2162
|
`;
|
|
2415
2163
|
}
|
|
2416
|
-
function createShellTractorsPage() {
|
|
2417
|
-
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2418
|
-
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2419
|
-
import ShellFrame from '../../shell-frame';
|
|
2420
|
-
import { Recommendations } from '../../vertical-components';
|
|
2421
|
-
import { ultramodernLocalisedUrls } from '../../ultramodern-route-metadata';
|
|
2422
|
-
|
|
2423
|
-
${createLocalizedHeadComponent()}
|
|
2424
|
-
export default function ShellTractorsPage() {
|
|
2425
|
-
return (
|
|
2426
|
-
<ShellFrame>
|
|
2427
|
-
<LocalizedHead />
|
|
2428
|
-
<Recommendations />
|
|
2429
|
-
</ShellFrame>
|
|
2430
|
-
);
|
|
2431
|
-
}
|
|
2432
|
-
`;
|
|
2433
|
-
}
|
|
2434
|
-
function createShellStoresPage() {
|
|
2435
|
-
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2436
|
-
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2437
|
-
import ShellFrame from '../../shell-frame';
|
|
2438
|
-
import { StorePicker } from '../../vertical-components';
|
|
2439
|
-
import { ultramodernLocalisedUrls } from '../../ultramodern-route-metadata';
|
|
2440
|
-
|
|
2441
|
-
${createLocalizedHeadComponent()}
|
|
2442
|
-
export default function ShellStoresPage() {
|
|
2443
|
-
return (
|
|
2444
|
-
<ShellFrame>
|
|
2445
|
-
<LocalizedHead />
|
|
2446
|
-
<StorePicker />
|
|
2447
|
-
</ShellFrame>
|
|
2448
|
-
);
|
|
2449
|
-
}
|
|
2450
|
-
`;
|
|
2451
|
-
}
|
|
2452
|
-
function createShellProductPage() {
|
|
2453
|
-
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2454
|
-
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2455
|
-
import ShellFrame from '../../../shell-frame';
|
|
2456
|
-
import { ProductPage } from '../../../vertical-components';
|
|
2457
|
-
import { ultramodernLocalisedUrls } from '../../../ultramodern-route-metadata';
|
|
2458
|
-
|
|
2459
|
-
${createLocalizedHeadComponent()}
|
|
2460
|
-
export default function ShellProductPage() {
|
|
2461
|
-
return (
|
|
2462
|
-
<ShellFrame boundary="decide">
|
|
2463
|
-
<LocalizedHead />
|
|
2464
|
-
<ProductPage />
|
|
2465
|
-
</ShellFrame>
|
|
2466
|
-
);
|
|
2467
|
-
}
|
|
2468
|
-
`;
|
|
2469
|
-
}
|
|
2470
|
-
function createShellCartPage() {
|
|
2471
|
-
return `import { Helmet } from '@modern-js/runtime/head';
|
|
2472
|
-
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2473
|
-
import ShellFrame from '../../shell-frame';
|
|
2474
|
-
import { CartPage } from '../../vertical-components';
|
|
2475
|
-
import { ultramodernLocalisedUrls } from '../../ultramodern-route-metadata';
|
|
2476
|
-
|
|
2477
|
-
${createLocalizedHeadComponent()}
|
|
2478
|
-
export default function ShellCartPage() {
|
|
2479
|
-
return (
|
|
2480
|
-
<ShellFrame boundary="checkout" showCart={false}>
|
|
2481
|
-
<LocalizedHead />
|
|
2482
|
-
<CartPage />
|
|
2483
|
-
</ShellFrame>
|
|
2484
|
-
);
|
|
2485
|
-
}
|
|
2486
|
-
`;
|
|
2487
|
-
}
|
|
2488
2164
|
function createShellFrameComponent() {
|
|
2489
2165
|
const tw = createTw(tailwindPrefixForApp(shellApp));
|
|
2490
2166
|
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2491
2167
|
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
2492
2168
|
import type { ReactNode } from 'react';
|
|
2493
|
-
import
|
|
2494
|
-
import { Header, MiniCart } from './vertical-components';
|
|
2169
|
+
import { Header, StatusBadge } from './vertical-components';
|
|
2495
2170
|
import { ultramodernLocalisedUrls } from './ultramodern-route-metadata';
|
|
2496
2171
|
|
|
2497
2172
|
const supportedLanguages = ['en', 'cs'] as const;
|
|
2498
2173
|
type SupportedLanguage = (typeof supportedLanguages)[number];
|
|
2499
2174
|
|
|
2500
2175
|
type ShellFrameProps = {
|
|
2501
|
-
boundary?: 'checkout' | 'decide' | 'explore';
|
|
2502
2176
|
children: ReactNode;
|
|
2503
|
-
showCart?: boolean;
|
|
2504
2177
|
};
|
|
2505
2178
|
|
|
2506
2179
|
const localisedUrls = ultramodernLocalisedUrls as Record<
|
|
@@ -2618,14 +2291,14 @@ const locationSuffix = (location: {
|
|
|
2618
2291
|
return \`\${locationSearch}\${locationHash}\`;
|
|
2619
2292
|
};
|
|
2620
2293
|
|
|
2621
|
-
export default function ShellFrame({
|
|
2294
|
+
export default function ShellFrame({ children }: ShellFrameProps) {
|
|
2622
2295
|
const { i18nInstance, language } = useModernI18n();
|
|
2623
2296
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2624
2297
|
const location = useLocation();
|
|
2625
2298
|
const suffix = locationSuffix(location);
|
|
2626
2299
|
|
|
2627
2300
|
return (
|
|
2628
|
-
<main className="${tw('min-h-screen bg-um-canvas px-4 py-5 text-um-foreground sm:px-6 lg:px-12')}"
|
|
2301
|
+
<main className="${tw('min-h-screen bg-um-canvas px-4 py-5 text-um-foreground sm:px-6 lg:px-12')}">
|
|
2629
2302
|
<div className="${tw('mx-auto flex min-h-20 max-w-7xl flex-col items-start gap-3 bg-white/90 px-4 py-3 shadow-xl shadow-stone-900/10 sm:px-6 md:flex-row md:flex-wrap md:items-center md:justify-between')}">
|
|
2630
2303
|
<Header />
|
|
2631
2304
|
<div className="${tw('flex min-w-0 flex-wrap items-center gap-2 md:ml-auto')}">
|
|
@@ -2654,204 +2327,33 @@ export default function ShellFrame({ boundary = 'explore', children, showCart =
|
|
|
2654
2327
|
🇨🇿
|
|
2655
2328
|
</option>
|
|
2656
2329
|
</select>
|
|
2657
|
-
|
|
2330
|
+
<StatusBadge />
|
|
2658
2331
|
</div>
|
|
2659
2332
|
</div>
|
|
2660
|
-
<BoundaryOverlay />
|
|
2661
2333
|
{children}
|
|
2662
2334
|
</main>
|
|
2663
2335
|
);
|
|
2664
2336
|
}
|
|
2665
2337
|
`;
|
|
2666
2338
|
}
|
|
2667
|
-
function
|
|
2668
|
-
const tw = createTw(tailwindPrefixForApp(shellApp));
|
|
2669
|
-
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2670
|
-
import { useEffect, useMemo, useState, type CSSProperties } from 'react';
|
|
2671
|
-
|
|
2672
|
-
type BoundaryConfig = {
|
|
2673
|
-
color: string;
|
|
2674
|
-
label: string;
|
|
2675
|
-
};
|
|
2676
|
-
|
|
2677
|
-
type BoundaryBox = BoundaryConfig & {
|
|
2678
|
-
height: number;
|
|
2679
|
-
id: string;
|
|
2680
|
-
labelPlacement: 'above' | 'edge' | 'inside';
|
|
2681
|
-
left: number;
|
|
2682
|
-
top: number;
|
|
2683
|
-
width: number;
|
|
2684
|
-
};
|
|
2685
|
-
|
|
2686
|
-
declare global {
|
|
2687
|
-
interface Window {
|
|
2688
|
-
__ULTRAMODERN_BOUNDARIES__?: Partial<Record<string, Partial<BoundaryConfig>>>;
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
|
-
|
|
2692
|
-
const defaultBoundaryColors = {
|
|
2693
|
-
checkout: 'var(--um-boundary-checkout, #f6cf45)',
|
|
2694
|
-
decide: 'var(--um-boundary-decide, #30e27a)',
|
|
2695
|
-
explore: 'var(--um-boundary-explore, #ff5a5f)',
|
|
2696
|
-
} as const;
|
|
2697
|
-
|
|
2698
|
-
const boundaryIds = ['explore', 'decide', 'checkout'] as const;
|
|
2699
|
-
|
|
2700
|
-
export default function BoundaryOverlay() {
|
|
2701
|
-
const { i18nInstance, language } = useModernI18n();
|
|
2702
|
-
const [mounted, setMounted] = useState(false);
|
|
2703
|
-
const [enabled, setEnabled] = useState(false);
|
|
2704
|
-
const [boxes, setBoxes] = useState<BoundaryBox[]>([]);
|
|
2705
|
-
const boundaryConfig = useMemo(() => {
|
|
2706
|
-
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2707
|
-
const runtimeOverrides =
|
|
2708
|
-
typeof window === 'undefined'
|
|
2709
|
-
? {}
|
|
2710
|
-
: (window.__ULTRAMODERN_BOUNDARIES__ ?? {});
|
|
2711
|
-
|
|
2712
|
-
return Object.fromEntries(
|
|
2713
|
-
boundaryIds.map(id => [
|
|
2714
|
-
id,
|
|
2715
|
-
{
|
|
2716
|
-
color: runtimeOverrides[id]?.color ?? defaultBoundaryColors[id],
|
|
2717
|
-
label: runtimeOverrides[id]?.label ?? t(\`shell.boundaries.\${id}\`),
|
|
2718
|
-
},
|
|
2719
|
-
]),
|
|
2720
|
-
) as Record<string, BoundaryConfig>;
|
|
2721
|
-
}, [i18nInstance, language]);
|
|
2722
|
-
const toggleLabel = i18nInstance['t'].bind(i18nInstance)(
|
|
2723
|
-
'shell.boundaries.toggle',
|
|
2724
|
-
);
|
|
2725
|
-
|
|
2726
|
-
useEffect(() => {
|
|
2727
|
-
setMounted(true);
|
|
2728
|
-
}, []);
|
|
2729
|
-
|
|
2730
|
-
useEffect(() => {
|
|
2731
|
-
if (!enabled) {
|
|
2732
|
-
setBoxes([]);
|
|
2733
|
-
return;
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
|
-
const readBoxes = () => {
|
|
2737
|
-
const nextBoxes = Array.from(
|
|
2738
|
-
document.querySelectorAll<HTMLElement>(
|
|
2739
|
-
'[data-mf-page-boundary], [data-mf-boundary]',
|
|
2740
|
-
),
|
|
2741
|
-
)
|
|
2742
|
-
.map((element, index) => {
|
|
2743
|
-
const pageBoundary = element.dataset.mfPageBoundary;
|
|
2744
|
-
const id = pageBoundary ?? element.dataset.mfBoundary ?? 'unknown';
|
|
2745
|
-
const rect = element.getBoundingClientRect();
|
|
2746
|
-
if (rect.width <= 0 || rect.height <= 0) {
|
|
2747
|
-
return undefined;
|
|
2748
|
-
}
|
|
2749
|
-
const fallback = {
|
|
2750
|
-
color: 'var(--um-boundary-unknown, #7c8cff)',
|
|
2751
|
-
label: id,
|
|
2752
|
-
};
|
|
2753
|
-
const config = boundaryConfig[id] ?? fallback;
|
|
2754
|
-
|
|
2755
|
-
return {
|
|
2756
|
-
...config,
|
|
2757
|
-
height: rect.height,
|
|
2758
|
-
id: \`\${id}-\${index}\`,
|
|
2759
|
-
labelPlacement: pageBoundary ? 'edge' : rect.height < 48 ? 'above' : 'inside',
|
|
2760
|
-
left: rect.left,
|
|
2761
|
-
top: rect.top,
|
|
2762
|
-
width: rect.width,
|
|
2763
|
-
} satisfies BoundaryBox;
|
|
2764
|
-
})
|
|
2765
|
-
.filter((box): box is BoundaryBox => box !== undefined);
|
|
2766
|
-
|
|
2767
|
-
setBoxes(nextBoxes);
|
|
2768
|
-
};
|
|
2769
|
-
|
|
2770
|
-
readBoxes();
|
|
2771
|
-
|
|
2772
|
-
const resizeObserver = new ResizeObserver(readBoxes);
|
|
2773
|
-
for (const element of document.querySelectorAll<HTMLElement>(
|
|
2774
|
-
'[data-mf-page-boundary], [data-mf-boundary]',
|
|
2775
|
-
)) {
|
|
2776
|
-
resizeObserver.observe(element);
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
const mutationObserver = new MutationObserver(readBoxes);
|
|
2780
|
-
mutationObserver.observe(document.body, {
|
|
2781
|
-
childList: true,
|
|
2782
|
-
subtree: true,
|
|
2783
|
-
});
|
|
2784
|
-
|
|
2785
|
-
window.addEventListener('resize', readBoxes);
|
|
2786
|
-
window.addEventListener('scroll', readBoxes, true);
|
|
2787
|
-
|
|
2788
|
-
return () => {
|
|
2789
|
-
mutationObserver.disconnect();
|
|
2790
|
-
resizeObserver.disconnect();
|
|
2791
|
-
window.removeEventListener('resize', readBoxes);
|
|
2792
|
-
window.removeEventListener('scroll', readBoxes, true);
|
|
2793
|
-
};
|
|
2794
|
-
}, [boundaryConfig, enabled]);
|
|
2795
|
-
|
|
2796
|
-
if (!mounted) {
|
|
2797
|
-
return null;
|
|
2798
|
-
}
|
|
2799
|
-
|
|
2800
|
-
return (
|
|
2801
|
-
<>
|
|
2802
|
-
<label className="${tw('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')}">
|
|
2803
|
-
<input
|
|
2804
|
-
className="${tw('size-4 accent-emerald-800')}"
|
|
2805
|
-
checked={enabled}
|
|
2806
|
-
onChange={event => setEnabled(event.currentTarget.checked)}
|
|
2807
|
-
type="checkbox"
|
|
2808
|
-
/>
|
|
2809
|
-
<span>{toggleLabel}</span>
|
|
2810
|
-
</label>
|
|
2811
|
-
{enabled ? (
|
|
2812
|
-
<div aria-hidden="true" className="${tw('pointer-events-none fixed inset-0 z-[70]')}">
|
|
2813
|
-
{boxes.map(box => (
|
|
2814
|
-
<div
|
|
2815
|
-
className="${tw('fixed rounded-lg border-2')}"
|
|
2816
|
-
data-label-placement={box.labelPlacement}
|
|
2817
|
-
key={box.id}
|
|
2818
|
-
style={
|
|
2819
|
-
{
|
|
2820
|
-
borderColor: box.color,
|
|
2821
|
-
boxShadow: \`0 0 0 1px rgba(255,255,255,.72), 0 6px 20px color-mix(in srgb, \${box.color} 20%, transparent)\`,
|
|
2822
|
-
height: box.height,
|
|
2823
|
-
left: box.left,
|
|
2824
|
-
top: box.top,
|
|
2825
|
-
width: box.width,
|
|
2826
|
-
} as CSSProperties
|
|
2827
|
-
}
|
|
2828
|
-
>
|
|
2829
|
-
<span
|
|
2830
|
-
className={\`${tw('absolute whitespace-nowrap rounded-full px-2 py-1 text-[0.7rem] font-black leading-none text-stone-950')} \${box.labelPlacement === 'above' ? '${tw('bottom-[calc(100%+0.25rem)] right-1 top-auto')}' : box.labelPlacement === 'edge' ? '${tw('left-0 top-28 -translate-x-[calc(100%-1px)] -rotate-90 rounded-b-none')}' : '${tw('right-1 top-1')}'}\`}
|
|
2831
|
-
style={{ backgroundColor: box.color }}
|
|
2832
|
-
>
|
|
2833
|
-
{box.label}
|
|
2834
|
-
</span>
|
|
2835
|
-
</div>
|
|
2836
|
-
))}
|
|
2837
|
-
</div>
|
|
2838
|
-
) : null}
|
|
2839
|
-
</>
|
|
2840
|
-
);
|
|
2841
|
-
}
|
|
2842
|
-
`;
|
|
2843
|
-
}
|
|
2844
|
-
function createShellRemoteComponents(scope) {
|
|
2339
|
+
function createShellRemoteComponents(scope, remotes = []) {
|
|
2845
2340
|
const tw = createTw(tailwindPrefixForApp(shellApp));
|
|
2341
|
+
const widgetRemotes = remotes.filter((remote)=>Object.hasOwn(remote.exposes ?? {}, './Widget'));
|
|
2342
|
+
const serverImports = widgetRemotes.map((remote)=>`import ${toPascalCase(remote.id)}WidgetServer from '${ultramodern_workspace_packageName(scope, remote.packageSuffix)}/Widget';`).join('\n');
|
|
2343
|
+
const hydratedExports = widgetRemotes.map((remote)=>{
|
|
2344
|
+
const componentName = `${toPascalCase(remote.id)}Widget`;
|
|
2345
|
+
return `const ${componentName} = createHydratedRemote(${componentName}Server, '${remoteDependencyAlias(remote)}/Widget');`;
|
|
2346
|
+
}).join('\n');
|
|
2347
|
+
const showcaseItems = widgetRemotes.map((remote)=>{
|
|
2348
|
+
const componentName = `${toPascalCase(remote.id)}Widget`;
|
|
2349
|
+
return ` <${componentName} key="${remote.id}" />`;
|
|
2350
|
+
}).join('\n');
|
|
2351
|
+
const remoteCount = String(widgetRemotes.length);
|
|
2846
2352
|
return `import { createLazyComponent } from '@module-federation/modern-js-v3/react';
|
|
2847
2353
|
import { getInstance, loadRemote } from '@module-federation/modern-js-v3/runtime';
|
|
2848
2354
|
import { Suspense, useEffect, useMemo, useState, type ComponentType } from 'react';
|
|
2849
|
-
import
|
|
2850
|
-
|
|
2851
|
-
import RecommendationsServer from '${ultramodern_workspace_packageName(scope, 'explore')}/Recommendations';
|
|
2852
|
-
import ProductPageServer from '${ultramodern_workspace_packageName(scope, 'decide')}/ProductPage';
|
|
2853
|
-
import MiniCartServer from '${ultramodern_workspace_packageName(scope, 'checkout')}/MiniCart';
|
|
2854
|
-
import CartPageServer from '${ultramodern_workspace_packageName(scope, 'checkout')}/CartPage';
|
|
2355
|
+
import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2356
|
+
${serverImports}
|
|
2855
2357
|
|
|
2856
2358
|
type RemoteComponentModule = {
|
|
2857
2359
|
default: ComponentType;
|
|
@@ -2866,8 +2368,11 @@ const loadRemoteComponent = async (specifier: string) => {
|
|
|
2866
2368
|
};
|
|
2867
2369
|
|
|
2868
2370
|
const remoteFallback =
|
|
2869
|
-
({ error }: { error: Error }) =>
|
|
2870
|
-
|
|
2371
|
+
({ error }: { error: Error }) => {
|
|
2372
|
+
const { i18nInstance } = useModernI18n();
|
|
2373
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2374
|
+
return <div className="${tw('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}>{t('shell.remoteUnavailable')}</div>;
|
|
2375
|
+
};
|
|
2871
2376
|
|
|
2872
2377
|
const createHydratedRemote = (
|
|
2873
2378
|
ServerComponent: ComponentType,
|
|
@@ -2909,12 +2414,50 @@ const createHydratedRemote = (
|
|
|
2909
2414
|
};
|
|
2910
2415
|
};
|
|
2911
2416
|
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
export
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2417
|
+
${hydratedExports}
|
|
2418
|
+
|
|
2419
|
+
export function Header() {
|
|
2420
|
+
const { i18nInstance } = useModernI18n();
|
|
2421
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2422
|
+
|
|
2423
|
+
return (
|
|
2424
|
+
<header className="${tw('flex min-w-0 flex-wrap items-center gap-x-8 gap-y-2 md:flex-1')}" data-modern-boundary-id="${shellApp.mfName}" data-modern-mf-expose="shell/Header">
|
|
2425
|
+
<I18nLink className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('shell.title')}</I18nLink>
|
|
2426
|
+
</header>
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
export function StatusBadge() {
|
|
2431
|
+
const { i18nInstance } = useModernI18n();
|
|
2432
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2433
|
+
|
|
2434
|
+
return (
|
|
2435
|
+
<span className="${tw('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 shadow-lg shadow-stone-900/5')}">
|
|
2436
|
+
{${remoteCount}} {t('shell.hero.cardOneKicker')}
|
|
2437
|
+
</span>
|
|
2438
|
+
);
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
export function VerticalShowcase() {
|
|
2442
|
+
const { i18nInstance } = useModernI18n();
|
|
2443
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2444
|
+
|
|
2445
|
+
if (${remoteCount} === 0) {
|
|
2446
|
+
return (
|
|
2447
|
+
<section className="${tw('mx-auto mt-12 max-w-7xl rounded-2xl bg-white/90 p-6 shadow-xl shadow-stone-900/10')}">
|
|
2448
|
+
<p className="${tw('text-lg font-bold text-stone-700')}">{t('shell.hero.empty')}</p>
|
|
2449
|
+
</section>
|
|
2450
|
+
);
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
return (
|
|
2454
|
+
<section className="${tw('mx-auto mt-12 max-w-7xl')}" data-modern-boundary-id="${shellApp.mfName}">
|
|
2455
|
+
<div className="${tw('grid gap-4 md:grid-cols-2')}">
|
|
2456
|
+
${showcaseItems}
|
|
2457
|
+
</div>
|
|
2458
|
+
</section>
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2918
2461
|
`;
|
|
2919
2462
|
}
|
|
2920
2463
|
function createRemotePage(app) {
|
|
@@ -2999,15 +2542,21 @@ export default function Layout() {
|
|
|
2999
2542
|
}
|
|
3000
2543
|
function createRemoteEntry(app) {
|
|
3001
2544
|
const tw = createTw(tailwindPrefixForApp(app));
|
|
3002
|
-
|
|
2545
|
+
const domain = app.domain ?? app.id;
|
|
2546
|
+
if (app.exposes?.['./RecordPage']) return `export { default } from './components/record-page';
|
|
3003
2547
|
`;
|
|
3004
|
-
if (app.exposes?.['./
|
|
2548
|
+
if (app.exposes?.['./ActionQueue']) return `export { default } from './components/action-queue';
|
|
3005
2549
|
`;
|
|
3006
|
-
return `
|
|
2550
|
+
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2551
|
+
|
|
2552
|
+
export default function ${toPascalCase(domain)}Route() {
|
|
2553
|
+
const { i18nInstance } = useModernI18n();
|
|
2554
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2555
|
+
|
|
3007
2556
|
return (
|
|
3008
2557
|
<section className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}" data-mf-remote="${app.id}" data-mf-expose="./Route">
|
|
3009
|
-
<h2 className="${tw('text-2xl font-black')}"
|
|
3010
|
-
<p className="${tw('mt-2 text-stone-600')}">
|
|
2558
|
+
<h2 className="${tw('text-2xl font-black')}">{t('${domain}.title')}</h2>
|
|
2559
|
+
<p className="${tw('mt-2 text-stone-600')}">{t('${domain}.routeSurface')}</p>
|
|
3011
2560
|
</section>
|
|
3012
2561
|
);
|
|
3013
2562
|
}
|
|
@@ -3015,13 +2564,18 @@ function createRemoteEntry(app) {
|
|
|
3015
2564
|
}
|
|
3016
2565
|
function createRemoteWidget(app) {
|
|
3017
2566
|
const tw = createTw(tailwindPrefixForApp(app));
|
|
3018
|
-
const
|
|
3019
|
-
const
|
|
3020
|
-
return `
|
|
2567
|
+
const domain = app.domain ?? app.id;
|
|
2568
|
+
const componentName = `${toPascalCase(domain)}Widget`;
|
|
2569
|
+
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2570
|
+
|
|
2571
|
+
export default function ${componentName}() {
|
|
2572
|
+
const { i18nInstance } = useModernI18n();
|
|
2573
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2574
|
+
|
|
3021
2575
|
return (
|
|
3022
2576
|
<section className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}" data-mf-remote="${app.id}">
|
|
3023
|
-
<h2 className="${tw('text-2xl font-black')}"
|
|
3024
|
-
<p className="${tw('mt-2 text-stone-600')}"
|
|
2577
|
+
<h2 className="${tw('text-2xl font-black')}">{t('${domain}.title')}</h2>
|
|
2578
|
+
<p className="${tw('mt-2 text-stone-600')}">{t('${domain}.widgetBody')}</p>
|
|
3025
2579
|
</section>
|
|
3026
2580
|
);
|
|
3027
2581
|
}
|
|
@@ -3029,90 +2583,88 @@ function createRemoteWidget(app) {
|
|
|
3029
2583
|
}
|
|
3030
2584
|
function createRemoteExposeComponent(app, expose) {
|
|
3031
2585
|
const tw = createTw(tailwindPrefixForApp(app));
|
|
3032
|
-
if ('
|
|
2586
|
+
if ('workspace' === app.id && './Header' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
3033
2587
|
|
|
3034
2588
|
export default function Header() {
|
|
3035
|
-
const { i18nInstance
|
|
2589
|
+
const { i18nInstance } = useModernI18n();
|
|
3036
2590
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3037
2591
|
|
|
3038
2592
|
return (
|
|
3039
|
-
<header className="${tw('flex min-w-0 flex-wrap items-center gap-x-8 gap-y-2 md:flex-1')}" data-
|
|
3040
|
-
<
|
|
3041
|
-
<nav aria-label={t('
|
|
3042
|
-
<
|
|
3043
|
-
<
|
|
2593
|
+
<header className="${tw('flex min-w-0 flex-wrap items-center gap-x-8 gap-y-2 md:flex-1')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">
|
|
2594
|
+
<I18nLink className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('workspace.header.brand')}</I18nLink>
|
|
2595
|
+
<nav aria-label={t('workspace.header.navigation')} className="${tw('flex items-center gap-5')}">
|
|
2596
|
+
<I18nLink className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/workspaces">{t('workspace.header.workspaces')}</I18nLink>
|
|
2597
|
+
<I18nLink className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/directory">{t('workspace.header.directory')}</I18nLink>
|
|
3044
2598
|
</nav>
|
|
3045
2599
|
</header>
|
|
3046
2600
|
);
|
|
3047
2601
|
}
|
|
3048
2602
|
`;
|
|
3049
|
-
if ('
|
|
2603
|
+
if ('workspace' === app.id && './Highlights' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
3050
2604
|
|
|
3051
|
-
const
|
|
3052
|
-
{ badge: '
|
|
3053
|
-
{ badge: '
|
|
3054
|
-
{ badge: '
|
|
3055
|
-
{ badge: 'explore.recommendations.vineyard', image: '${commerceAssetUrl('vineyard.svg')}', name: 'Vineyard Narrow 80', slug: 'vineyard-narrow-80' },
|
|
2605
|
+
const highlights = [
|
|
2606
|
+
{ badge: 'workspace.highlights.shell', href: '/workspaces', name: 'workspace.highlights.shellTitle' },
|
|
2607
|
+
{ badge: 'workspace.highlights.records', href: '/records/starter-record', name: 'workspace.highlights.recordsTitle' },
|
|
2608
|
+
{ badge: 'workspace.highlights.actions', href: '/actions', name: 'workspace.highlights.actionsTitle' },
|
|
3056
2609
|
] as const;
|
|
3057
2610
|
|
|
3058
|
-
export default function
|
|
3059
|
-
const { i18nInstance
|
|
2611
|
+
export default function Highlights() {
|
|
2612
|
+
const { i18nInstance } = useModernI18n();
|
|
3060
2613
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3061
2614
|
|
|
3062
2615
|
return (
|
|
3063
|
-
<section className="${tw('mx-auto mt-12 max-w-7xl')}" data-
|
|
3064
|
-
<h2 className="${tw('text-3xl font-black tracking-normal text-stone-950')}">{t('
|
|
3065
|
-
<div className="${tw('mt-5 grid gap-4 md:grid-cols-
|
|
3066
|
-
{
|
|
3067
|
-
<
|
|
3068
|
-
<
|
|
3069
|
-
<
|
|
3070
|
-
|
|
3071
|
-
</a>
|
|
2616
|
+
<section className="${tw('mx-auto mt-12 max-w-7xl')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">
|
|
2617
|
+
<h2 className="${tw('text-3xl font-black tracking-normal text-stone-950')}">{t('workspace.highlights.title')}</h2>
|
|
2618
|
+
<div className="${tw('mt-5 grid gap-4 md:grid-cols-3')}">
|
|
2619
|
+
{highlights.map(highlight => (
|
|
2620
|
+
<I18nLink className="${tw('block rounded-2xl bg-white/90 p-5 text-stone-950 no-underline shadow-xl shadow-stone-900/10 transition hover:-translate-y-0.5 hover:shadow-2xl')}" key={highlight.href} to={highlight.href}>
|
|
2621
|
+
<span className="${tw('text-xs font-black uppercase tracking-[0.16em] text-emerald-800')}">{t(highlight.badge)}</span>
|
|
2622
|
+
<strong className="${tw('mt-3 block text-xl font-black leading-tight')}">{t(highlight.name)}</strong>
|
|
2623
|
+
</I18nLink>
|
|
3072
2624
|
))}
|
|
3073
2625
|
</div>
|
|
3074
2626
|
</section>
|
|
3075
2627
|
);
|
|
3076
2628
|
}
|
|
3077
2629
|
`;
|
|
3078
|
-
if ('
|
|
3079
|
-
|
|
3080
|
-
const fieldLoaderImage = '${commerceAssetUrl('field-loader.svg')}';
|
|
3081
|
-
const vineyardImage = '${commerceAssetUrl('vineyard.svg')}';
|
|
2630
|
+
if ('workspace' === app.id && './DirectoryPanel' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
3082
2631
|
|
|
3083
|
-
export default function
|
|
2632
|
+
export default function DirectoryPanel() {
|
|
3084
2633
|
const { i18nInstance } = useModernI18n();
|
|
3085
2634
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3086
2635
|
|
|
3087
2636
|
return (
|
|
3088
|
-
<section className="${tw('mx-auto mt-12 max-w-7xl')}" data-
|
|
3089
|
-
<h2 className="${tw('text-3xl font-black tracking-normal text-stone-950')}">{t('
|
|
2637
|
+
<section className="${tw('mx-auto mt-12 max-w-7xl')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">
|
|
2638
|
+
<h2 className="${tw('text-3xl font-black tracking-normal text-stone-950')}">{t('workspace.directory.title')}</h2>
|
|
3090
2639
|
<div className="${tw('mt-5 grid gap-4 md:grid-cols-2')}">
|
|
3091
|
-
<article className="${tw('rounded-2xl bg-white/90 p-
|
|
3092
|
-
<
|
|
3093
|
-
<
|
|
3094
|
-
<
|
|
2640
|
+
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}">
|
|
2641
|
+
<span className="${tw('text-xs font-black uppercase tracking-[0.16em] text-emerald-800')}">{t('workspace.directory.platformTeam')}</span>
|
|
2642
|
+
<strong className="${tw('mt-2 block text-2xl font-black')}">{t('workspace.directory.platformName')}</strong>
|
|
2643
|
+
<p className="${tw('mt-2 text-sm font-semibold text-stone-600')}">{t('workspace.directory.platformCopy')}</p>
|
|
3095
2644
|
</article>
|
|
3096
|
-
<article className="${tw('rounded-2xl bg-white/90 p-
|
|
3097
|
-
<
|
|
3098
|
-
<
|
|
3099
|
-
<
|
|
2645
|
+
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}">
|
|
2646
|
+
<span className="${tw('text-xs font-black uppercase tracking-[0.16em] text-emerald-800')}">{t('workspace.directory.deliveryTeam')}</span>
|
|
2647
|
+
<strong className="${tw('mt-2 block text-2xl font-black')}">{t('workspace.directory.deliveryName')}</strong>
|
|
2648
|
+
<p className="${tw('mt-2 text-sm font-semibold text-stone-600')}">{t('workspace.directory.deliveryCopy')}</p>
|
|
3100
2649
|
</article>
|
|
3101
2650
|
</div>
|
|
3102
2651
|
</section>
|
|
3103
2652
|
);
|
|
3104
2653
|
}
|
|
3105
2654
|
`;
|
|
3106
|
-
if ('
|
|
3107
|
-
|
|
2655
|
+
if ('workspace' === app.id && './Footer' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2656
|
+
|
|
2657
|
+
export default function Footer() {
|
|
2658
|
+
const { i18nInstance } = useModernI18n();
|
|
2659
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2660
|
+
|
|
2661
|
+
return <footer className="${tw('mx-auto mt-12 max-w-7xl text-sm font-bold text-stone-600')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">{t('workspace.footer')}</footer>;
|
|
3108
2662
|
}
|
|
3109
2663
|
`;
|
|
3110
2664
|
if ('./Widget' === expose) return createRemoteWidget(app);
|
|
3111
2665
|
const componentName = `${toPascalCase(app.domain ?? app.id)}${toPascalCase(expose.replace(/^\.\//u, ''))}`;
|
|
3112
|
-
if ('
|
|
3113
|
-
import {
|
|
3114
|
-
|
|
3115
|
-
const fieldLoaderImage = '${commerceAssetUrl('field-loader.svg')}';
|
|
2666
|
+
if ('records' === app.id && './RecordPage' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2667
|
+
import { Highlights, StartAction } from './vertical-components';
|
|
3116
2668
|
|
|
3117
2669
|
export default function ${componentName}() {
|
|
3118
2670
|
const { i18nInstance } = useModernI18n();
|
|
@@ -3120,94 +2672,98 @@ export default function ${componentName}() {
|
|
|
3120
2672
|
|
|
3121
2673
|
return (
|
|
3122
2674
|
<>
|
|
3123
|
-
<section className="${tw('mx-auto mt-10 grid max-w-7xl items-center gap-8 md:grid-cols-[1fr_0.95fr] lg:gap-14')}" data-
|
|
3124
|
-
<
|
|
2675
|
+
<section className="${tw('mx-auto mt-10 grid max-w-7xl items-center gap-8 md:grid-cols-[1fr_0.95fr] lg:gap-14')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}" data-mf-remote="${app.id}">
|
|
2676
|
+
<div className="${tw('rounded-3xl border-[18px] border-amber-200 bg-white/90 p-8 shadow-2xl shadow-stone-900/15')}">
|
|
2677
|
+
<p className="${tw('text-xs font-black uppercase tracking-[0.18em] text-emerald-800')}">{t('records.record.lifecycle')}</p>
|
|
2678
|
+
<dl className="${tw('mt-6 grid gap-4')}">
|
|
2679
|
+
<div><dt className="${tw('text-sm font-bold text-stone-500')}">{t('records.record.owner')}</dt><dd className="${tw('text-xl font-black')}">{t('records.record.ownerName')}</dd></div>
|
|
2680
|
+
<div><dt className="${tw('text-sm font-bold text-stone-500')}">{t('records.record.state')}</dt><dd className="${tw('text-xl font-black')}">{t('records.record.ready')}</dd></div>
|
|
2681
|
+
</dl>
|
|
2682
|
+
</div>
|
|
3125
2683
|
<div>
|
|
3126
|
-
<p className="${tw('text-xs font-black uppercase tracking-[0.18em] text-emerald-800')}">{t('
|
|
3127
|
-
<h1 className="${tw('mt-3 text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl')}">
|
|
3128
|
-
<p className="${tw('mt-5 max-w-2xl text-lg leading-8 text-stone-600')}">{t('
|
|
2684
|
+
<p className="${tw('text-xs font-black uppercase tracking-[0.18em] text-emerald-800')}">{t('records.record.eyebrow')}</p>
|
|
2685
|
+
<h1 className="${tw('mt-3 text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl')}">{t('records.record.title')}</h1>
|
|
2686
|
+
<p className="${tw('mt-5 max-w-2xl text-lg leading-8 text-stone-600')}">{t('records.record.lede')}</p>
|
|
3129
2687
|
<div className="${tw('mt-8 grid gap-4 sm:grid-cols-3')}">
|
|
3130
|
-
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}"><span className="${tw('block text-sm font-bold text-stone-500')}">{t('
|
|
3131
|
-
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}"><span className="${tw('block text-sm font-bold text-stone-500')}">{t('
|
|
3132
|
-
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}"><span className="${tw('block text-sm font-bold text-stone-500')}">{t('
|
|
2688
|
+
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}"><span className="${tw('block text-sm font-bold text-stone-500')}">{t('records.record.priority')}</span><strong className="${tw('mt-2 block text-lg font-black')}">{t('records.record.priorityValue')}</strong></article>
|
|
2689
|
+
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}"><span className="${tw('block text-sm font-bold text-stone-500')}">{t('records.record.sla')}</span><strong className="${tw('mt-2 block text-lg font-black')}">{t('records.record.slaValue')}</strong></article>
|
|
2690
|
+
<article className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}"><span className="${tw('block text-sm font-bold text-stone-500')}">{t('records.record.status')}</span><strong className="${tw('mt-2 block text-lg font-black')}">{t('records.record.ready')}</strong></article>
|
|
3133
2691
|
</div>
|
|
3134
|
-
<
|
|
2692
|
+
<StartAction />
|
|
3135
2693
|
</div>
|
|
3136
2694
|
</section>
|
|
3137
|
-
<
|
|
2695
|
+
<Highlights />
|
|
3138
2696
|
</>
|
|
3139
2697
|
);
|
|
3140
2698
|
}
|
|
3141
2699
|
`;
|
|
3142
|
-
if ('
|
|
3143
|
-
import {
|
|
2700
|
+
if ('actions' === app.id && './StartAction' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2701
|
+
import { useActionQueue } from '../action-queue-store';
|
|
3144
2702
|
|
|
3145
2703
|
export default function ${componentName}() {
|
|
3146
|
-
const { i18nInstance
|
|
2704
|
+
const { i18nInstance } = useModernI18n();
|
|
3147
2705
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3148
|
-
const
|
|
2706
|
+
const queue = useActionQueue();
|
|
3149
2707
|
|
|
3150
2708
|
return (
|
|
3151
|
-
<div className="${tw('mt-8 flex flex-wrap gap-3')}" data-
|
|
3152
|
-
<button className="${tw('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={
|
|
3153
|
-
{t('
|
|
2709
|
+
<div className="${tw('mt-8 flex flex-wrap gap-3')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">
|
|
2710
|
+
<button className="${tw('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={queue.addStarterAction} type="button">
|
|
2711
|
+
{t('actions.controls.start')}
|
|
3154
2712
|
</button>
|
|
3155
|
-
<
|
|
3156
|
-
{t('
|
|
3157
|
-
</
|
|
2713
|
+
<I18nLink className="${tw('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')}" to="/actions">
|
|
2714
|
+
{t('actions.controls.viewQueue')}
|
|
2715
|
+
</I18nLink>
|
|
3158
2716
|
</div>
|
|
3159
2717
|
);
|
|
3160
2718
|
}
|
|
3161
2719
|
`;
|
|
3162
|
-
if ('
|
|
3163
|
-
import {
|
|
2720
|
+
if ('actions' === app.id && './StatusBadge' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2721
|
+
import { useActionQueue } from '../action-queue-store';
|
|
3164
2722
|
|
|
3165
2723
|
export default function ${componentName}() {
|
|
3166
|
-
const { i18nInstance
|
|
2724
|
+
const { i18nInstance } = useModernI18n();
|
|
3167
2725
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3168
|
-
const
|
|
3169
|
-
const count = cart.lines.reduce((sum, line) => sum + line.quantity, 0);
|
|
2726
|
+
const queue = useActionQueue();
|
|
3170
2727
|
|
|
3171
2728
|
return (
|
|
3172
|
-
<
|
|
3173
|
-
{t('
|
|
3174
|
-
</
|
|
2729
|
+
<span className="${tw('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 shadow-lg shadow-stone-900/5')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">
|
|
2730
|
+
{t('actions.queue.itemCount', { count: queue.lines.length })}
|
|
2731
|
+
</span>
|
|
3175
2732
|
);
|
|
3176
2733
|
}
|
|
3177
2734
|
`;
|
|
3178
|
-
if ('
|
|
3179
|
-
import {
|
|
2735
|
+
if ('actions' === app.id && './ActionQueue' === expose) return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2736
|
+
import { useActionQueue } from '../action-queue-store';
|
|
3180
2737
|
|
|
3181
2738
|
export default function ${componentName}() {
|
|
3182
2739
|
const { i18nInstance } = useModernI18n();
|
|
3183
2740
|
const t = i18nInstance['t'].bind(i18nInstance);
|
|
3184
|
-
const
|
|
2741
|
+
const queue = useActionQueue();
|
|
3185
2742
|
|
|
3186
2743
|
return (
|
|
3187
|
-
<section className="${tw('mx-auto mt-10 max-w-7xl')}" data-
|
|
3188
|
-
<h1 className="${tw('text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl')}">{t('
|
|
2744
|
+
<section className="${tw('mx-auto mt-10 max-w-7xl')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}" data-mf-remote="${app.id}">
|
|
2745
|
+
<h1 className="${tw('text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl')}">{t('actions.queue.title')}</h1>
|
|
3189
2746
|
<div className="${tw('mt-8 rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}">
|
|
3190
|
-
{
|
|
3191
|
-
<p>{t('
|
|
2747
|
+
{queue.lines.length === 0 ? (
|
|
2748
|
+
<p>{t('actions.queue.empty')}</p>
|
|
3192
2749
|
) : (
|
|
3193
2750
|
<>
|
|
3194
|
-
{
|
|
2751
|
+
{queue.lines.map(line => (
|
|
3195
2752
|
<article className="${tw('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}>
|
|
3196
2753
|
<div>
|
|
3197
|
-
<strong className="${tw('text-lg font-black')}">{line.
|
|
3198
|
-
<p className="${tw('text-stone-600')}">
|
|
2754
|
+
<strong className="${tw('text-lg font-black')}">{t(line.nameKey)}</strong>
|
|
2755
|
+
<p className="${tw('text-stone-600')}">{t(\`actions.queue.status.\${line.status}\`)}</p>
|
|
3199
2756
|
</div>
|
|
3200
2757
|
<div className="${tw('flex flex-wrap items-center gap-2')}">
|
|
3201
|
-
<button className="${tw('inline-flex
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
<button className="${tw('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={() =>
|
|
3205
|
-
{t('
|
|
2758
|
+
<button className="${tw('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={() => queue.complete(line.id)} type="button">
|
|
2759
|
+
{t('actions.controls.complete')}
|
|
2760
|
+
</button>
|
|
2761
|
+
<button className="${tw('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={() => queue.remove(line.id)} type="button">
|
|
2762
|
+
{t('actions.controls.remove')}
|
|
3206
2763
|
</button>
|
|
3207
2764
|
</div>
|
|
3208
2765
|
</article>
|
|
3209
2766
|
))}
|
|
3210
|
-
<p><strong>{t('checkout.cart.total')}: EUR {cart.total.toLocaleString('en-US')}</strong></p>
|
|
3211
2767
|
</>
|
|
3212
2768
|
)}
|
|
3213
2769
|
</div>
|
|
@@ -3215,23 +2771,34 @@ export default function ${componentName}() {
|
|
|
3215
2771
|
);
|
|
3216
2772
|
}
|
|
3217
2773
|
`;
|
|
3218
|
-
return `export default
|
|
2774
|
+
if ('actions' === app.id && './ActionReviewPage' === expose) return `export { default } from './action-queue';
|
|
2775
|
+
`;
|
|
2776
|
+
if ('actions' === app.id && './ActionSuccessPage' === expose) return `export { default } from './action-queue';
|
|
2777
|
+
`;
|
|
2778
|
+
const domain = app.domain ?? app.id;
|
|
2779
|
+
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2780
|
+
|
|
2781
|
+
export default function ${componentName}() {
|
|
2782
|
+
const { i18nInstance } = useModernI18n();
|
|
2783
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2784
|
+
|
|
3219
2785
|
return (
|
|
3220
|
-
<section className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}" data-
|
|
3221
|
-
<h2 className="${tw('text-2xl font-black')}"
|
|
3222
|
-
<p className="${tw('mt-2 text-stone-600')}">
|
|
2786
|
+
<section className="${tw('rounded-2xl bg-white/90 p-5 shadow-xl shadow-stone-900/10')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}" data-mf-remote="${app.id}">
|
|
2787
|
+
<h2 className="${tw('text-2xl font-black')}">{t('${domain}.title')}</h2>
|
|
2788
|
+
<p className="${tw('mt-2 text-stone-600')}">{t('${domain}.federatedSurface')}</p>
|
|
3223
2789
|
</section>
|
|
3224
2790
|
);
|
|
3225
2791
|
}
|
|
3226
2792
|
`;
|
|
3227
2793
|
}
|
|
3228
|
-
function
|
|
2794
|
+
function createRecordsRemoteComponents(scope, app) {
|
|
3229
2795
|
const tw = createTw(tailwindPrefixForApp(app));
|
|
3230
2796
|
return `import { createLazyComponent } from '@module-federation/modern-js-v3/react';
|
|
3231
2797
|
import { getInstance, loadRemote } from '@module-federation/modern-js-v3/runtime';
|
|
3232
2798
|
import { Suspense, useEffect, useMemo, useState, type ComponentType } from 'react';
|
|
3233
|
-
import
|
|
3234
|
-
import
|
|
2799
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
2800
|
+
import HighlightsServer from '${ultramodern_workspace_packageName(scope, 'workspace')}/Highlights';
|
|
2801
|
+
import StartActionServer from '${ultramodern_workspace_packageName(scope, 'actions')}/StartAction';
|
|
3235
2802
|
|
|
3236
2803
|
type RemoteComponentModule = {
|
|
3237
2804
|
default: ComponentType;
|
|
@@ -3246,8 +2813,11 @@ const loadRemoteComponent = async (specifier: string) => {
|
|
|
3246
2813
|
};
|
|
3247
2814
|
|
|
3248
2815
|
const remoteFallback =
|
|
3249
|
-
({ error }: { error: Error }) =>
|
|
3250
|
-
|
|
2816
|
+
({ error }: { error: Error }) => {
|
|
2817
|
+
const { i18nInstance } = useModernI18n();
|
|
2818
|
+
const t = i18nInstance['t'].bind(i18nInstance);
|
|
2819
|
+
return <div className="${tw('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}>{t('records.remoteUnavailable')}</div>;
|
|
2820
|
+
};
|
|
3251
2821
|
|
|
3252
2822
|
const createHydratedRemote = (
|
|
3253
2823
|
ServerComponent: ComponentType,
|
|
@@ -3289,8 +2859,8 @@ const createHydratedRemote = (
|
|
|
3289
2859
|
};
|
|
3290
2860
|
};
|
|
3291
2861
|
|
|
3292
|
-
export const
|
|
3293
|
-
export const
|
|
2862
|
+
export const Highlights = createHydratedRemote(HighlightsServer, 'workspace/Highlights');
|
|
2863
|
+
export const StartAction = createHydratedRemote(StartActionServer, 'actions/StartAction');
|
|
3294
2864
|
`;
|
|
3295
2865
|
}
|
|
3296
2866
|
function remoteComponentOutputPath(app, expose) {
|
|
@@ -3298,216 +2868,345 @@ function remoteComponentOutputPath(app, expose) {
|
|
|
3298
2868
|
if (!exposePath?.startsWith('./src/components/')) return;
|
|
3299
2869
|
return `${app.directory}/${exposePath.replace(/^\.\//u, '')}`;
|
|
3300
2870
|
}
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
2871
|
+
const commonLocaleMessages = {
|
|
2872
|
+
cs: {
|
|
2873
|
+
language: {
|
|
2874
|
+
cs: 'Čeština',
|
|
2875
|
+
en: 'Angličtina',
|
|
2876
|
+
switcher: 'Jazyk'
|
|
3306
2877
|
},
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
2878
|
+
routes: {
|
|
2879
|
+
actions: 'Akce',
|
|
2880
|
+
directory: 'Adresář',
|
|
2881
|
+
done: 'Akce dokončena',
|
|
2882
|
+
home: 'Domů',
|
|
2883
|
+
recordDetail: 'Detail záznamu',
|
|
2884
|
+
review: 'Revize akce',
|
|
2885
|
+
unavailable: 'Nedostupné',
|
|
2886
|
+
workspaces: 'Pracovní prostory'
|
|
2887
|
+
}
|
|
2888
|
+
},
|
|
2889
|
+
en: {
|
|
2890
|
+
language: {
|
|
2891
|
+
cs: 'Czech',
|
|
2892
|
+
en: 'English',
|
|
2893
|
+
switcher: 'Language'
|
|
3310
2894
|
},
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
2895
|
+
routes: {
|
|
2896
|
+
actions: 'Actions',
|
|
2897
|
+
directory: 'Directory',
|
|
2898
|
+
done: 'Action complete',
|
|
2899
|
+
home: 'Home',
|
|
2900
|
+
recordDetail: 'Record detail',
|
|
2901
|
+
review: 'Action review',
|
|
2902
|
+
unavailable: 'Unavailable',
|
|
2903
|
+
workspaces: 'Workspaces'
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
const generatedLocaleResources = {
|
|
2908
|
+
cs: {
|
|
2909
|
+
actions: {
|
|
2910
|
+
...commonLocaleMessages.cs,
|
|
2911
|
+
federatedSurface: 'Federovaná plocha vlastněná tímto verticalem.',
|
|
2912
|
+
remoteUnavailable: 'Remote vertical je nedostupný',
|
|
2913
|
+
role: 'akce',
|
|
2914
|
+
routeSurface: 'Routovaná plocha vlastněná tímto verticalem.',
|
|
2915
|
+
title: 'Akční vertical',
|
|
2916
|
+
widgetBody: 'Vlastní routovanou plochu verticalu.',
|
|
2917
|
+
controls: {
|
|
2918
|
+
complete: 'Dokončit',
|
|
2919
|
+
remove: 'Odebrat',
|
|
2920
|
+
start: 'Spustit akci',
|
|
2921
|
+
viewQueue: 'Zobrazit frontu'
|
|
2922
|
+
},
|
|
2923
|
+
queue: {
|
|
2924
|
+
empty: 'Zatím nejsou ve frontě žádné akce.',
|
|
2925
|
+
itemCount_few: '{{count}} akce',
|
|
2926
|
+
itemCount_many: '{{count}} akce',
|
|
2927
|
+
itemCount_one: '{{count}} akce',
|
|
2928
|
+
itemCount_other: '{{count}} akcí',
|
|
2929
|
+
starterAction: 'Zkontrolovat startovací záznam',
|
|
2930
|
+
status: {
|
|
2931
|
+
complete: 'Dokončeno',
|
|
2932
|
+
queued: 'Ve frontě'
|
|
2933
|
+
},
|
|
2934
|
+
title: 'Fronta akcí'
|
|
2935
|
+
}
|
|
3314
2936
|
},
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
2937
|
+
records: {
|
|
2938
|
+
...commonLocaleMessages.cs,
|
|
2939
|
+
federatedSurface: 'Federovaná plocha vlastněná tímto verticalem.',
|
|
2940
|
+
remoteUnavailable: 'Remote vertical je nedostupný',
|
|
2941
|
+
role: 'záznamy',
|
|
2942
|
+
routeSurface: 'Routovaná plocha vlastněná tímto verticalem.',
|
|
2943
|
+
title: 'Záznamový vertical',
|
|
2944
|
+
widgetBody: 'Vlastní routovanou plochu verticalu.',
|
|
2945
|
+
record: {
|
|
2946
|
+
eyebrow: 'Detail záznamu',
|
|
2947
|
+
lede: 'Startovací záznam ověřuje spolupráci lokalizovaného SSR, hydratace remote části a Effect BFF vlastněného verticalem.',
|
|
2948
|
+
lifecycle: 'Životní cyklus',
|
|
2949
|
+
owner: 'Vlastník',
|
|
2950
|
+
ownerName: 'Zkušenost pracovního prostoru',
|
|
2951
|
+
priority: 'Priorita',
|
|
2952
|
+
priorityValue: 'P1',
|
|
2953
|
+
ready: 'Připraveno',
|
|
2954
|
+
sla: 'SLA',
|
|
2955
|
+
slaValue: '24 h',
|
|
2956
|
+
state: 'Stav',
|
|
2957
|
+
status: 'Status',
|
|
2958
|
+
title: 'Startovací záznam'
|
|
2959
|
+
}
|
|
3318
2960
|
},
|
|
3319
|
-
identity: {
|
|
3320
|
-
role: 'identita',
|
|
3321
|
-
title: 'Identitní remote'
|
|
3322
|
-
}
|
|
3323
|
-
};
|
|
3324
|
-
if ('shell' === app.kind) return {
|
|
3325
2961
|
shell: {
|
|
2962
|
+
boundaries: {
|
|
2963
|
+
toggle: 'zobrazit hranice verticalů'
|
|
2964
|
+
},
|
|
3326
2965
|
hero: {
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
2966
|
+
cardOne: 'Přidejte první business vertical příkazem create <domain> --vertical, až ho opravdu potřebujete.',
|
|
2967
|
+
cardOneKicker: 'Verticaly',
|
|
2968
|
+
cardTwo: 'Plný markup, styly a lokalizovaný obsah se vykreslí před hydratací.',
|
|
2969
|
+
cardTwoKicker: 'Vykreslení',
|
|
2970
|
+
empty: 'Zatím nejsou připojené žádné MicroVerticaly.',
|
|
2971
|
+
eyebrow: 'Shell SuperApp starter',
|
|
2972
|
+
lede: 'Začněte s produkčně připraveným shellem. MicroVerticaly přidávejte až podle skutečných business domén.',
|
|
2973
|
+
primary: 'Shell je připraven',
|
|
2974
|
+
secondary: 'Přidejte vertical, až bude potřeba'
|
|
2975
|
+
},
|
|
2976
|
+
language: commonLocaleMessages.cs.language,
|
|
2977
|
+
remoteUnavailable: 'Remote vertical je nedostupný',
|
|
2978
|
+
remotes: {},
|
|
2979
|
+
routes: {
|
|
2980
|
+
home: commonLocaleMessages.cs.routes.home
|
|
2981
|
+
},
|
|
2982
|
+
title: 'UltraModern Workspace'
|
|
2983
|
+
},
|
|
2984
|
+
workspace: {
|
|
2985
|
+
...commonLocaleMessages.cs,
|
|
2986
|
+
federatedSurface: 'Federovaná plocha vlastněná tímto verticalem.',
|
|
2987
|
+
footer: 'UltraModern workspace',
|
|
2988
|
+
remoteUnavailable: 'Remote vertical je nedostupný',
|
|
2989
|
+
role: 'pracovní prostor',
|
|
2990
|
+
routeSurface: 'Routovaná plocha vlastněná tímto verticalem.',
|
|
2991
|
+
title: 'Pracovní vertical',
|
|
2992
|
+
widgetBody: 'Poskytuje sdílené UI prvky pro pracovní prostor.',
|
|
2993
|
+
directory: {
|
|
2994
|
+
deliveryCopy: 'Vlastní generované akční toky a stav workflow.',
|
|
2995
|
+
deliveryName: 'Doručovací operace',
|
|
2996
|
+
deliveryTeam: 'Doručovací tým',
|
|
2997
|
+
platformCopy: 'Vlastní skládání shellu, routování a sdílený zážitek.',
|
|
2998
|
+
platformName: 'Platformní zkušenost',
|
|
2999
|
+
platformTeam: 'Platformní tým',
|
|
3000
|
+
title: 'Adresář'
|
|
3331
3001
|
},
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3002
|
+
header: {
|
|
3003
|
+
brand: 'UltraModern Workspace',
|
|
3004
|
+
directory: 'Adresář',
|
|
3005
|
+
navigation: 'Hlavní navigace',
|
|
3006
|
+
workspaces: 'Pracovní prostory'
|
|
3336
3007
|
},
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3008
|
+
highlights: {
|
|
3009
|
+
actions: 'Akční část',
|
|
3010
|
+
actionsTitle: 'Spusťte akci napříč verticaly',
|
|
3011
|
+
records: 'Záznamová část',
|
|
3012
|
+
recordsTitle: 'Otevřete záznam vlastněný routou',
|
|
3013
|
+
shell: 'Shell část',
|
|
3014
|
+
shellTitle: 'Skládejte verticaly v shellu',
|
|
3015
|
+
title: 'Generované plochy verticalů'
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
},
|
|
3019
|
+
en: {
|
|
3020
|
+
actions: {
|
|
3021
|
+
...commonLocaleMessages.en,
|
|
3022
|
+
federatedSurface: 'Federated surface owned by this vertical.',
|
|
3023
|
+
remoteUnavailable: 'Remote vertical unavailable',
|
|
3024
|
+
role: 'actions',
|
|
3025
|
+
routeSurface: 'Route surface owned by this vertical.',
|
|
3026
|
+
title: 'Actions Vertical',
|
|
3027
|
+
widgetBody: 'Owns a vertical route surface.',
|
|
3028
|
+
controls: {
|
|
3029
|
+
complete: 'Complete',
|
|
3030
|
+
remove: 'Remove',
|
|
3031
|
+
start: 'Start action',
|
|
3032
|
+
viewQueue: 'View queue'
|
|
3341
3033
|
},
|
|
3034
|
+
queue: {
|
|
3035
|
+
empty: 'No actions are queued yet.',
|
|
3036
|
+
itemCount_one: '{{count}} action',
|
|
3037
|
+
itemCount_other: '{{count}} actions',
|
|
3038
|
+
starterAction: 'Review starter record',
|
|
3039
|
+
status: {
|
|
3040
|
+
complete: 'Complete',
|
|
3041
|
+
queued: 'Queued'
|
|
3042
|
+
},
|
|
3043
|
+
title: 'Action queue'
|
|
3044
|
+
}
|
|
3045
|
+
},
|
|
3046
|
+
records: {
|
|
3047
|
+
...commonLocaleMessages.en,
|
|
3048
|
+
federatedSurface: 'Federated surface owned by this vertical.',
|
|
3049
|
+
remoteUnavailable: 'Remote vertical unavailable',
|
|
3050
|
+
role: 'records',
|
|
3051
|
+
routeSurface: 'Route surface owned by this vertical.',
|
|
3052
|
+
title: 'Records Vertical',
|
|
3053
|
+
widgetBody: 'Owns a vertical route surface.',
|
|
3054
|
+
record: {
|
|
3055
|
+
eyebrow: 'Record detail',
|
|
3056
|
+
lede: 'A starter record proving localized SSR, remote hydration, and a vertical-owned Effect BFF can cooperate.',
|
|
3057
|
+
lifecycle: 'Lifecycle',
|
|
3058
|
+
owner: 'Owner',
|
|
3059
|
+
ownerName: 'Workspace Experience',
|
|
3060
|
+
priority: 'Priority',
|
|
3061
|
+
priorityValue: 'P1',
|
|
3062
|
+
ready: 'Ready',
|
|
3063
|
+
sla: 'SLA',
|
|
3064
|
+
slaValue: '24h',
|
|
3065
|
+
state: 'State',
|
|
3066
|
+
status: 'Status',
|
|
3067
|
+
title: 'Starter Record'
|
|
3068
|
+
}
|
|
3069
|
+
},
|
|
3070
|
+
shell: {
|
|
3342
3071
|
boundaries: {
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3072
|
+
toggle: 'show vertical boundaries'
|
|
3073
|
+
},
|
|
3074
|
+
hero: {
|
|
3075
|
+
cardOne: 'Add the first business vertical with create <domain> --vertical when the product needs one.',
|
|
3076
|
+
cardOneKicker: 'Verticals',
|
|
3077
|
+
cardTwo: 'Full page markup, styles, and localized content render before hydration.',
|
|
3078
|
+
cardTwoKicker: 'Rendering',
|
|
3079
|
+
empty: 'No MicroVerticals are connected yet.',
|
|
3080
|
+
eyebrow: 'Shell SuperApp starter',
|
|
3081
|
+
lede: 'Start with a production-ready shell. Add MicroVerticals later for real business domains.',
|
|
3082
|
+
primary: 'Shell ready',
|
|
3083
|
+
secondary: 'Add a vertical when needed'
|
|
3347
3084
|
},
|
|
3085
|
+
language: commonLocaleMessages.en.language,
|
|
3086
|
+
remoteUnavailable: 'Remote vertical unavailable',
|
|
3087
|
+
remotes: {},
|
|
3348
3088
|
routes: {
|
|
3349
|
-
|
|
3350
|
-
home: 'en' === language ? 'Home' : 'Domů',
|
|
3351
|
-
listing: 'en' === language ? 'Tractors' : 'Traktory',
|
|
3352
|
-
productDetail: 'en' === language ? 'Tractor detail' : 'Detail traktoru',
|
|
3353
|
-
storePicker: 'en' === language ? 'Stores' : 'Prodejci'
|
|
3089
|
+
home: commonLocaleMessages.en.routes.home
|
|
3354
3090
|
},
|
|
3355
|
-
title: '
|
|
3356
|
-
}
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3091
|
+
title: 'UltraModern Workspace'
|
|
3092
|
+
},
|
|
3093
|
+
workspace: {
|
|
3094
|
+
...commonLocaleMessages.en,
|
|
3095
|
+
federatedSurface: 'Federated surface owned by this vertical.',
|
|
3096
|
+
footer: 'UltraModern workspace',
|
|
3097
|
+
remoteUnavailable: 'Remote vertical unavailable',
|
|
3098
|
+
role: 'workspace',
|
|
3099
|
+
routeSurface: 'Route surface owned by this vertical.',
|
|
3100
|
+
title: 'Workspace Vertical',
|
|
3101
|
+
widgetBody: 'Provides shared UI primitives for the workspace.',
|
|
3102
|
+
directory: {
|
|
3103
|
+
deliveryCopy: 'Owns generated action flows and workflow state.',
|
|
3104
|
+
deliveryName: 'Delivery Operations',
|
|
3105
|
+
deliveryTeam: 'Delivery team',
|
|
3106
|
+
platformCopy: 'Owns shell composition, routing, and shared experience.',
|
|
3107
|
+
platformName: 'Platform Experience',
|
|
3108
|
+
platformTeam: 'Platform team',
|
|
3109
|
+
title: 'Directory'
|
|
3369
3110
|
},
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
listing: 'en' === language ? 'Tractors' : 'Traktory',
|
|
3376
|
-
productDetail: 'en' === language ? 'Tractor detail' : 'Detail traktoru',
|
|
3377
|
-
storePicker: 'en' === language ? 'Store picker' : 'Výběr prodejce',
|
|
3378
|
-
thankYou: 'en' === language ? 'Order confirmation' : 'Potvrzení objednávky',
|
|
3379
|
-
unavailable: 'en' === language ? 'Unavailable' : 'Nedostupné'
|
|
3111
|
+
header: {
|
|
3112
|
+
brand: 'UltraModern Workspace',
|
|
3113
|
+
directory: 'Directory',
|
|
3114
|
+
navigation: 'Main navigation',
|
|
3115
|
+
workspaces: 'Workspaces'
|
|
3380
3116
|
},
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
bestRows: 'en' === language ? 'Best for tight rows' : 'Nejlepší do úzkých řádků',
|
|
3391
|
-
loaderReady: 'en' === language ? 'Loader-ready' : 'Připraveno pro nakladač',
|
|
3392
|
-
title: 'en' === language ? 'Compare alternatives' : 'Porovnat alternativy',
|
|
3393
|
-
vineyard: 'en' === language ? 'Vineyard profile' : 'Profil pro vinice'
|
|
3394
|
-
},
|
|
3395
|
-
stores: {
|
|
3396
|
-
northRegion: 'en' === language ? 'North region' : 'Severní region',
|
|
3397
|
-
southRegion: 'en' === language ? 'South region' : 'Jižní region',
|
|
3398
|
-
title: 'en' === language ? 'Stores' : 'Prodejci'
|
|
3399
|
-
}
|
|
3400
|
-
} : {},
|
|
3401
|
-
...'decide' === domain ? {
|
|
3402
|
-
product: {
|
|
3403
|
-
availability: 'en' === language ? 'Availability' : 'Dostupnost',
|
|
3404
|
-
eyebrow: 'en' === language ? 'Machine detail' : 'Detail stroje',
|
|
3405
|
-
inStock: 'en' === language ? 'In stock' : 'Skladem',
|
|
3406
|
-
lede: 'en' === language ? 'A loader-ready tractor for feed, hay, gravel, and winter road work.' : 'Traktor připravený pro nakladač na krmivo, seno, štěrk a zimní údržbu cest.',
|
|
3407
|
-
power: 'en' === language ? 'Power' : 'Výkon',
|
|
3408
|
-
price: 'en' === language ? 'Price' : 'Cena'
|
|
3409
|
-
}
|
|
3410
|
-
} : {},
|
|
3411
|
-
...'checkout' === domain ? {
|
|
3412
|
-
actions: {
|
|
3413
|
-
addToCart: 'en' === language ? 'Add to cart' : 'Přidat do košíku',
|
|
3414
|
-
remove: 'en' === language ? 'Remove' : 'Odebrat',
|
|
3415
|
-
viewCart: 'en' === language ? 'View cart' : 'Zobrazit košík'
|
|
3416
|
-
},
|
|
3417
|
-
cart: {
|
|
3418
|
-
empty: 'en' === language ? 'Your cart is empty.' : 'Košík je prázdný.',
|
|
3419
|
-
title: 'en' === language ? 'Your cart' : 'Váš košík',
|
|
3420
|
-
total: 'en' === language ? 'Total' : 'Celkem'
|
|
3421
|
-
}
|
|
3422
|
-
} : {}
|
|
3117
|
+
highlights: {
|
|
3118
|
+
actions: 'Action lane',
|
|
3119
|
+
actionsTitle: 'Trigger a cross-vertical action',
|
|
3120
|
+
records: 'Record lane',
|
|
3121
|
+
recordsTitle: 'Open a route-owned record',
|
|
3122
|
+
shell: 'Shell lane',
|
|
3123
|
+
shellTitle: 'Compose verticals in the shell',
|
|
3124
|
+
title: 'Generated vertical surfaces'
|
|
3125
|
+
}
|
|
3423
3126
|
}
|
|
3127
|
+
}
|
|
3128
|
+
};
|
|
3129
|
+
const createFallbackLocaleMessages = (app, language)=>({
|
|
3130
|
+
...commonLocaleMessages[language],
|
|
3131
|
+
federatedSurface: generatedLocaleResources[language].workspace.federatedSurface,
|
|
3132
|
+
remoteUnavailable: generatedLocaleResources[language].workspace.remoteUnavailable,
|
|
3133
|
+
role: app.domain ?? app.kind,
|
|
3134
|
+
routeSurface: generatedLocaleResources[language].workspace.routeSurface,
|
|
3135
|
+
title: app.displayName,
|
|
3136
|
+
widgetBody: 'vertical' === app.kind ? generatedLocaleResources[language].records.widgetBody : generatedLocaleResources[language].workspace.widgetBody
|
|
3137
|
+
});
|
|
3138
|
+
function createAppLocaleMessages(app, language) {
|
|
3139
|
+
const domain = app.domain ?? app.id;
|
|
3140
|
+
const messageKey = 'shell' === app.kind ? 'shell' : domain;
|
|
3141
|
+
const messages = generatedLocaleResources[language][messageKey] ?? createFallbackLocaleMessages(app, language);
|
|
3142
|
+
return {
|
|
3143
|
+
[messageKey]: messages
|
|
3424
3144
|
};
|
|
3425
3145
|
}
|
|
3426
|
-
function
|
|
3427
|
-
|
|
3428
|
-
return
|
|
3429
|
-
return (
|
|
3430
|
-
<button className="${tw('rounded-full text-um-foreground')}" type="button">
|
|
3431
|
-
{label}
|
|
3432
|
-
</button>
|
|
3433
|
-
);
|
|
3434
|
-
}
|
|
3435
|
-
`;
|
|
3436
|
-
}
|
|
3437
|
-
function createDesignTokens() {
|
|
3438
|
-
return `export const designTokens = {
|
|
3439
|
-
color: {
|
|
3440
|
-
accent: '#2f8f68',
|
|
3441
|
-
foreground: '#133225',
|
|
3442
|
-
},
|
|
3443
|
-
radius: {
|
|
3444
|
-
control: '999px',
|
|
3445
|
-
},
|
|
3446
|
-
} as const;
|
|
3447
|
-
`;
|
|
3146
|
+
function createAppPublicLocaleMessages(app, language, remotes = []) {
|
|
3147
|
+
if ('shell' !== app.kind) return createAppLocaleMessages(app, language);
|
|
3148
|
+
return Object.assign({}, createAppLocaleMessages(app, language), ...remotes.map((remote)=>createAppLocaleMessages(remote, language)));
|
|
3448
3149
|
}
|
|
3449
|
-
function
|
|
3150
|
+
function createActionQueueStore() {
|
|
3450
3151
|
return `import { useEffect, useMemo, useState } from 'react';
|
|
3451
3152
|
|
|
3452
|
-
export type
|
|
3153
|
+
export type ActionLine = {
|
|
3453
3154
|
id: string;
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
quantity: number;
|
|
3155
|
+
nameKey: string;
|
|
3156
|
+
status: 'queued' | 'complete';
|
|
3457
3157
|
};
|
|
3458
3158
|
|
|
3459
|
-
const storageKey = 'ultramodern-
|
|
3460
|
-
const
|
|
3461
|
-
const
|
|
3462
|
-
id: '
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
quantity: 1,
|
|
3159
|
+
const storageKey = 'ultramodern-action-queue';
|
|
3160
|
+
const queueEvent = 'ultramodern-action-queue-change';
|
|
3161
|
+
const starterAction: ActionLine = {
|
|
3162
|
+
id: 'starter-action',
|
|
3163
|
+
nameKey: 'actions.queue.starterAction',
|
|
3164
|
+
status: 'queued',
|
|
3466
3165
|
};
|
|
3467
3166
|
|
|
3468
|
-
const
|
|
3167
|
+
const readQueue = (): ActionLine[] => {
|
|
3469
3168
|
if (typeof window === 'undefined') {
|
|
3470
3169
|
return [];
|
|
3471
3170
|
}
|
|
3472
3171
|
|
|
3473
3172
|
try {
|
|
3474
3173
|
const value = window.localStorage.getItem(storageKey);
|
|
3475
|
-
return value ? (JSON.parse(value) as
|
|
3174
|
+
return value ? (JSON.parse(value) as ActionLine[]) : [];
|
|
3476
3175
|
} catch {
|
|
3477
3176
|
return [];
|
|
3478
3177
|
}
|
|
3479
3178
|
};
|
|
3480
3179
|
|
|
3481
|
-
const
|
|
3180
|
+
const writeQueue = (lines: ActionLine[]) => {
|
|
3482
3181
|
if (typeof window === 'undefined') {
|
|
3483
3182
|
return;
|
|
3484
3183
|
}
|
|
3485
3184
|
|
|
3486
3185
|
window.localStorage.setItem(storageKey, JSON.stringify(lines));
|
|
3487
|
-
window.dispatchEvent(new CustomEvent(
|
|
3186
|
+
window.dispatchEvent(new CustomEvent(queueEvent));
|
|
3488
3187
|
};
|
|
3489
3188
|
|
|
3490
3189
|
const updateLine = (
|
|
3491
3190
|
id: string,
|
|
3492
|
-
updater: (line:
|
|
3191
|
+
updater: (line: ActionLine) => ActionLine | undefined,
|
|
3493
3192
|
) => {
|
|
3494
|
-
const next =
|
|
3193
|
+
const next = readQueue()
|
|
3495
3194
|
.map(line => (line.id === id ? updater(line) : line))
|
|
3496
|
-
.filter((line): line is
|
|
3497
|
-
|
|
3195
|
+
.filter((line): line is ActionLine => Boolean(line));
|
|
3196
|
+
writeQueue(next);
|
|
3498
3197
|
};
|
|
3499
3198
|
|
|
3500
|
-
export function
|
|
3501
|
-
const [lines, setLines] = useState<
|
|
3199
|
+
export function useActionQueue() {
|
|
3200
|
+
const [lines, setLines] = useState<ActionLine[]>(() => readQueue());
|
|
3502
3201
|
|
|
3503
3202
|
useEffect(() => {
|
|
3504
|
-
const refresh = () => setLines(
|
|
3505
|
-
window.addEventListener(
|
|
3203
|
+
const refresh = () => setLines(readQueue());
|
|
3204
|
+
window.addEventListener(queueEvent, refresh);
|
|
3506
3205
|
window.addEventListener('storage', refresh);
|
|
3507
3206
|
refresh();
|
|
3508
3207
|
|
|
3509
3208
|
return () => {
|
|
3510
|
-
window.removeEventListener(
|
|
3209
|
+
window.removeEventListener(queueEvent, refresh);
|
|
3511
3210
|
window.removeEventListener('storage', refresh);
|
|
3512
3211
|
};
|
|
3513
3212
|
}, []);
|
|
@@ -3515,27 +3214,22 @@ export function useCartLines() {
|
|
|
3515
3214
|
return useMemo(
|
|
3516
3215
|
() => ({
|
|
3517
3216
|
lines,
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
const
|
|
3521
|
-
|
|
3522
|
-
writeCart(
|
|
3217
|
+
addStarterAction: () => {
|
|
3218
|
+
const existing = readQueue();
|
|
3219
|
+
const match = existing.find(line => line.id === starterAction.id);
|
|
3220
|
+
writeQueue(
|
|
3523
3221
|
match
|
|
3524
3222
|
? existing.map(line =>
|
|
3525
|
-
line.id ===
|
|
3526
|
-
? { ...line,
|
|
3223
|
+
line.id === starterAction.id
|
|
3224
|
+
? { ...line, status: 'queued' as const }
|
|
3527
3225
|
: line,
|
|
3528
3226
|
)
|
|
3529
|
-
: [...existing,
|
|
3227
|
+
: [...existing, starterAction],
|
|
3530
3228
|
);
|
|
3531
3229
|
},
|
|
3532
|
-
|
|
3533
|
-
updateLine(id, line => ({ ...line,
|
|
3534
|
-
|
|
3535
|
-
updateLine(id, line =>
|
|
3536
|
-
line.quantity > 1 ? { ...line, quantity: line.quantity - 1 } : undefined,
|
|
3537
|
-
),
|
|
3538
|
-
remove: (id: string) => writeCart(readCart().filter(line => line.id !== id)),
|
|
3230
|
+
complete: (id: string) =>
|
|
3231
|
+
updateLine(id, line => ({ ...line, status: 'complete' as const })),
|
|
3232
|
+
remove: (id: string) => writeQueue(readQueue().filter(line => line.id !== id)),
|
|
3539
3233
|
}),
|
|
3540
3234
|
[lines],
|
|
3541
3235
|
);
|
|
@@ -3552,36 +3246,35 @@ function createSharedDesignTokensCss() {
|
|
|
3552
3246
|
}
|
|
3553
3247
|
`;
|
|
3554
3248
|
}
|
|
3555
|
-
function
|
|
3249
|
+
function verticalEffectApiExport(service) {
|
|
3556
3250
|
return `${toCamelCase(effectApiStem(service))}EffectApi`;
|
|
3557
3251
|
}
|
|
3558
|
-
function
|
|
3252
|
+
function verticalEffectGroupName(service) {
|
|
3559
3253
|
return toCamelCase(effectApiStem(service));
|
|
3560
3254
|
}
|
|
3561
|
-
function
|
|
3255
|
+
function verticalEffectApiName(service) {
|
|
3562
3256
|
return `${toPascalCase(effectApiStem(service))}EffectApi`;
|
|
3563
3257
|
}
|
|
3564
|
-
function
|
|
3258
|
+
function verticalEffectSchemaExport(service) {
|
|
3565
3259
|
return `${toCamelCase(effectApiStem(service))}ItemSchema`;
|
|
3566
3260
|
}
|
|
3567
|
-
function
|
|
3261
|
+
function verticalEffectMarkerSchemaExport(service) {
|
|
3568
3262
|
return `${toCamelCase(effectApiStem(service))}MarkerSchema`;
|
|
3569
3263
|
}
|
|
3570
|
-
function
|
|
3264
|
+
function verticalEffectReadinessSchemaExport(service) {
|
|
3571
3265
|
return `${toCamelCase(effectApiStem(service))}ReadinessSchema`;
|
|
3572
3266
|
}
|
|
3573
|
-
function
|
|
3574
|
-
|
|
3575
|
-
return 'recommendations' === stem ? 'recommendation' : stem;
|
|
3267
|
+
function verticalEffectErrorStem(service) {
|
|
3268
|
+
return effectApiStem(service);
|
|
3576
3269
|
}
|
|
3577
|
-
function
|
|
3270
|
+
function verticalEffectCreatePayloadSchemaExport(service) {
|
|
3578
3271
|
return `${toCamelCase(effectApiStem(service))}CreatePayloadSchema`;
|
|
3579
3272
|
}
|
|
3580
|
-
function
|
|
3581
|
-
return `${toPascalCase(
|
|
3273
|
+
function verticalEffectNotFoundErrorExport(service) {
|
|
3274
|
+
return `${toPascalCase(verticalEffectErrorStem(service))}NotFound`;
|
|
3582
3275
|
}
|
|
3583
|
-
function
|
|
3584
|
-
return `${toCamelCase(
|
|
3276
|
+
function verticalEffectNotFoundSchemaExport(service) {
|
|
3277
|
+
return `${toCamelCase(verticalEffectErrorStem(service))}NotFoundSchema`;
|
|
3585
3278
|
}
|
|
3586
3279
|
function createEffectSharedApiImports() {
|
|
3587
3280
|
return `import {
|
|
@@ -3594,17 +3287,17 @@ function createEffectSharedApiImports() {
|
|
|
3594
3287
|
`;
|
|
3595
3288
|
}
|
|
3596
3289
|
function createEffectSharedApiContract(service) {
|
|
3597
|
-
const schemaExport =
|
|
3598
|
-
const markerSchemaExport =
|
|
3599
|
-
const readinessSchemaExport =
|
|
3600
|
-
const createPayloadSchemaExport =
|
|
3601
|
-
const notFoundErrorExport =
|
|
3602
|
-
const notFoundSchemaExport =
|
|
3603
|
-
const apiExport =
|
|
3604
|
-
const apiName =
|
|
3605
|
-
const groupName =
|
|
3290
|
+
const schemaExport = verticalEffectSchemaExport(service);
|
|
3291
|
+
const markerSchemaExport = verticalEffectMarkerSchemaExport(service);
|
|
3292
|
+
const readinessSchemaExport = verticalEffectReadinessSchemaExport(service);
|
|
3293
|
+
const createPayloadSchemaExport = verticalEffectCreatePayloadSchemaExport(service);
|
|
3294
|
+
const notFoundErrorExport = verticalEffectNotFoundErrorExport(service);
|
|
3295
|
+
const notFoundSchemaExport = verticalEffectNotFoundSchemaExport(service);
|
|
3296
|
+
const apiExport = verticalEffectApiExport(service);
|
|
3297
|
+
const apiName = verticalEffectApiName(service);
|
|
3298
|
+
const groupName = verticalEffectGroupName(service);
|
|
3606
3299
|
const stem = effectApiStem(service);
|
|
3607
|
-
const
|
|
3300
|
+
const apiPrefix = effectApiPrefix(service);
|
|
3608
3301
|
return `export const ${markerSchemaExport} = Schema.Struct({
|
|
3609
3302
|
appId: Schema.String,
|
|
3610
3303
|
packageName: Schema.String,
|
|
@@ -3719,10 +3412,10 @@ export const ${groupName}OperationContexts = {
|
|
|
3719
3412
|
} satisfies Record<string, OperationContext>;
|
|
3720
3413
|
|
|
3721
3414
|
export const ${groupName}ApiContract = {
|
|
3722
|
-
|
|
3415
|
+
apiPrefix: '${apiPrefix}',
|
|
3416
|
+
basePath: '${apiPrefix}/effect/${stem}',
|
|
3723
3417
|
ownerId: '${service.id}',
|
|
3724
|
-
|
|
3725
|
-
readinessPath: '${servicePrefix}/effect/${stem}/readiness',
|
|
3418
|
+
readinessPath: '${apiPrefix}/effect/${stem}/readiness',
|
|
3726
3419
|
} as const;
|
|
3727
3420
|
`;
|
|
3728
3421
|
}
|
|
@@ -3730,14 +3423,14 @@ function createEffectSharedApi(service) {
|
|
|
3730
3423
|
if (service) return `${createEffectSharedApiImports()}
|
|
3731
3424
|
${createEffectSharedApiContract(service)}`;
|
|
3732
3425
|
return `export const sharedEffectApiPackage = {
|
|
3733
|
-
scope: 'external-effect-
|
|
3426
|
+
scope: 'external-effect-api-contracts',
|
|
3734
3427
|
} as const;
|
|
3735
3428
|
`;
|
|
3736
3429
|
}
|
|
3737
3430
|
function createEffectServiceEntry(scope, service, contractImportPath = ultramodern_workspace_packageName(scope, 'shared-effect-api')) {
|
|
3738
|
-
const apiExport =
|
|
3739
|
-
const groupName =
|
|
3740
|
-
const notFoundErrorExport =
|
|
3431
|
+
const apiExport = verticalEffectApiExport(service);
|
|
3432
|
+
const groupName = verticalEffectGroupName(service);
|
|
3433
|
+
const notFoundErrorExport = verticalEffectNotFoundErrorExport(service);
|
|
3741
3434
|
const stem = effectApiStem(service);
|
|
3742
3435
|
return `import {
|
|
3743
3436
|
defineEffectBff,
|
|
@@ -3850,11 +3543,11 @@ export default defineEffectBff({
|
|
|
3850
3543
|
`;
|
|
3851
3544
|
}
|
|
3852
3545
|
function createEffectClient(service, contractImportPath) {
|
|
3853
|
-
const apiExport =
|
|
3854
|
-
const contractExport =
|
|
3546
|
+
const apiExport = verticalEffectApiExport(service);
|
|
3547
|
+
const contractExport = verticalEffectGroupName(service);
|
|
3855
3548
|
const stem = effectApiStem(service);
|
|
3856
|
-
const groupName =
|
|
3857
|
-
const singular =
|
|
3549
|
+
const groupName = verticalEffectGroupName(service);
|
|
3550
|
+
const singular = verticalEffectErrorStem(service);
|
|
3858
3551
|
const clientOptionsName = `${toPascalCase(stem)}ClientOptions`;
|
|
3859
3552
|
const createClientName = `create${toPascalCase(stem)}Client`;
|
|
3860
3553
|
const listName = `list${toPascalCase(stem)}`;
|
|
@@ -3883,7 +3576,7 @@ export function ${createClientName}(
|
|
|
3883
3576
|
options: ${clientOptionsName} = {},
|
|
3884
3577
|
) {
|
|
3885
3578
|
return makeEffectHttpApiClient(${apiExport}, {
|
|
3886
|
-
baseUrl: options.baseUrl ?? ${contractExport}ApiContract.
|
|
3579
|
+
baseUrl: options.baseUrl ?? ${contractExport}ApiContract.apiPrefix,
|
|
3887
3580
|
});
|
|
3888
3581
|
}
|
|
3889
3582
|
|
|
@@ -3950,33 +3643,21 @@ export function ${createName}(
|
|
|
3950
3643
|
}
|
|
3951
3644
|
`;
|
|
3952
3645
|
}
|
|
3953
|
-
function createShellEffectClient(scope) {
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
listDecide,
|
|
3969
|
-
type DecideClientOptions,
|
|
3970
|
-
} from '${ultramodern_workspace_packageName(scope, 'decide')}/effect/client';
|
|
3971
|
-
|
|
3972
|
-
export {
|
|
3973
|
-
createExplore,
|
|
3974
|
-
createExploreClient,
|
|
3975
|
-
getExplore,
|
|
3976
|
-
getExploreReadiness,
|
|
3977
|
-
listExplore,
|
|
3978
|
-
type ExploreClientOptions,
|
|
3979
|
-
} from '${ultramodern_workspace_packageName(scope, 'explore')}/effect/client';
|
|
3646
|
+
function createShellEffectClient(scope, remotes = []) {
|
|
3647
|
+
const exports = verticalEffectApps(remotes).map((remote)=>{
|
|
3648
|
+
const stem = effectApiStem(remote);
|
|
3649
|
+
const pascalStem = toPascalCase(stem);
|
|
3650
|
+
const pascalSingular = toPascalCase(verticalEffectErrorStem(remote));
|
|
3651
|
+
return `export {
|
|
3652
|
+
create${pascalSingular},
|
|
3653
|
+
create${pascalStem}Client,
|
|
3654
|
+
get${pascalSingular},
|
|
3655
|
+
get${pascalStem}Readiness,
|
|
3656
|
+
list${pascalStem},
|
|
3657
|
+
type ${pascalStem}ClientOptions,
|
|
3658
|
+
} from '${ultramodern_workspace_packageName(scope, remote.packageSuffix)}/effect/client';`;
|
|
3659
|
+
}).join('\n\n');
|
|
3660
|
+
return exports ? `${exports}\n` : `export const ultramodernVerticalClients = [] as const;
|
|
3980
3661
|
`;
|
|
3981
3662
|
}
|
|
3982
3663
|
function toPascalCase(value) {
|
|
@@ -4015,71 +3696,71 @@ function createEffectRequestContextContract() {
|
|
|
4015
3696
|
}
|
|
4016
3697
|
function createEffectDomainOperations(app) {
|
|
4017
3698
|
const stem = effectApiStem(app);
|
|
4018
|
-
const group =
|
|
3699
|
+
const group = verticalEffectGroupName(app);
|
|
4019
3700
|
const basePath = `/effect/${stem}`;
|
|
4020
|
-
if ('
|
|
4021
|
-
|
|
4022
|
-
client: '
|
|
3701
|
+
if ('actions' === stem) return {
|
|
3702
|
+
actionQueue: {
|
|
3703
|
+
client: 'listActions',
|
|
4023
3704
|
method: 'GET',
|
|
4024
3705
|
path: basePath,
|
|
4025
|
-
resource: '
|
|
3706
|
+
resource: 'action-queue',
|
|
4026
3707
|
owner: app.id
|
|
4027
3708
|
},
|
|
4028
|
-
|
|
4029
|
-
client: '
|
|
3709
|
+
actionMutation: {
|
|
3710
|
+
client: 'createActions',
|
|
4030
3711
|
method: 'POST',
|
|
4031
3712
|
path: basePath,
|
|
4032
|
-
resource: '
|
|
3713
|
+
resource: 'action',
|
|
4033
3714
|
owner: app.id
|
|
4034
3715
|
},
|
|
4035
|
-
|
|
4036
|
-
client: '
|
|
3716
|
+
actionStatus: {
|
|
3717
|
+
client: 'getActions',
|
|
4037
3718
|
method: 'GET',
|
|
4038
3719
|
path: `${basePath}/:id`,
|
|
4039
|
-
resource: '
|
|
3720
|
+
resource: 'action-status',
|
|
4040
3721
|
owner: app.id
|
|
4041
3722
|
}
|
|
4042
3723
|
};
|
|
4043
|
-
if ('
|
|
4044
|
-
|
|
4045
|
-
client: '
|
|
3724
|
+
if ('records' === stem) return {
|
|
3725
|
+
recordDetail: {
|
|
3726
|
+
client: 'getRecords',
|
|
4046
3727
|
method: 'GET',
|
|
4047
3728
|
path: `${basePath}/:id`,
|
|
4048
|
-
resource: '
|
|
3729
|
+
resource: 'record',
|
|
4049
3730
|
owner: app.id
|
|
4050
3731
|
},
|
|
4051
|
-
|
|
4052
|
-
client: '
|
|
3732
|
+
recordDraft: {
|
|
3733
|
+
client: 'createRecords',
|
|
4053
3734
|
method: 'POST',
|
|
4054
3735
|
path: basePath,
|
|
4055
|
-
resource: '
|
|
3736
|
+
resource: 'record-draft',
|
|
4056
3737
|
owner: app.id
|
|
4057
3738
|
},
|
|
4058
|
-
|
|
4059
|
-
client: '
|
|
3739
|
+
recordList: {
|
|
3740
|
+
client: 'listRecords',
|
|
4060
3741
|
method: 'GET',
|
|
4061
3742
|
path: basePath,
|
|
4062
|
-
resource: '
|
|
3743
|
+
resource: 'records',
|
|
4063
3744
|
owner: app.id
|
|
4064
3745
|
}
|
|
4065
3746
|
};
|
|
4066
3747
|
return {
|
|
4067
|
-
|
|
3748
|
+
workspaceFeed: {
|
|
4068
3749
|
client: `list${toPascalCase(stem)}`,
|
|
4069
3750
|
method: 'GET',
|
|
4070
3751
|
path: basePath,
|
|
4071
|
-
resource: '
|
|
3752
|
+
resource: 'workspace-items',
|
|
4072
3753
|
owner: app.id
|
|
4073
3754
|
},
|
|
4074
|
-
|
|
4075
|
-
client: `get${toPascalCase(
|
|
3755
|
+
workspaceDetail: {
|
|
3756
|
+
client: `get${toPascalCase(verticalEffectErrorStem(app))}`,
|
|
4076
3757
|
method: 'GET',
|
|
4077
3758
|
path: `${basePath}/:id`,
|
|
4078
|
-
resource: '
|
|
3759
|
+
resource: 'workspace-item',
|
|
4079
3760
|
owner: app.id
|
|
4080
3761
|
},
|
|
4081
|
-
|
|
4082
|
-
client: `create${toPascalCase(
|
|
3762
|
+
workspaceCreate: {
|
|
3763
|
+
client: `create${toPascalCase(verticalEffectErrorStem(app))}`,
|
|
4083
3764
|
method: 'POST',
|
|
4084
3765
|
path: basePath,
|
|
4085
3766
|
resource: group,
|
|
@@ -4113,28 +3794,29 @@ function effectApiTopologyMetadata(app) {
|
|
|
4113
3794
|
}
|
|
4114
3795
|
};
|
|
4115
3796
|
}
|
|
4116
|
-
function createTopology(scope) {
|
|
3797
|
+
function createTopology(scope, remotes = []) {
|
|
3798
|
+
const shellHost = createShellHost(remotes);
|
|
4117
3799
|
return {
|
|
4118
3800
|
schemaVersion: 1,
|
|
4119
3801
|
id: 'ultramodern-superapp-workspace-reference-topology',
|
|
4120
|
-
description: 'Generated UltraModern
|
|
3802
|
+
description: 'Generated UltraModern SuperApp shell that can grow by adding full-stack verticals.',
|
|
4121
3803
|
preset: 'presetUltramodern',
|
|
4122
3804
|
shell: {
|
|
4123
3805
|
id: shellApp.id,
|
|
4124
3806
|
kind: 'shell',
|
|
4125
3807
|
package: ultramodern_workspace_packageName(scope, shellApp.packageSuffix),
|
|
4126
|
-
verticalRefs:
|
|
3808
|
+
verticalRefs: shellHost.verticalRefs,
|
|
4127
3809
|
moduleFederation: {
|
|
4128
3810
|
role: 'host',
|
|
4129
3811
|
name: shellApp.mfName,
|
|
4130
|
-
remotes: createModuleFederationRemoteContracts(
|
|
3812
|
+
remotes: createModuleFederationRemoteContracts(shellHost, remotes),
|
|
4131
3813
|
ssr: true,
|
|
4132
3814
|
sharedContractVersion: 'mf-ssr-contract-v1'
|
|
4133
3815
|
},
|
|
4134
3816
|
cloudflare: createCloudflareDeployContract(scope, shellApp),
|
|
4135
3817
|
ownership: shellApp.ownership
|
|
4136
3818
|
},
|
|
4137
|
-
verticals:
|
|
3819
|
+
verticals: remotes.map((vertical)=>({
|
|
4138
3820
|
id: vertical.id,
|
|
4139
3821
|
kind: vertical.kind,
|
|
4140
3822
|
domain: vertical.domain,
|
|
@@ -4173,13 +3855,13 @@ function createTopology(scope) {
|
|
|
4173
3855
|
}
|
|
4174
3856
|
};
|
|
4175
3857
|
}
|
|
4176
|
-
function createOwnership(scope) {
|
|
3858
|
+
function createOwnership(scope, remotes = []) {
|
|
4177
3859
|
return {
|
|
4178
3860
|
schemaVersion: 1,
|
|
4179
3861
|
preset: 'presetUltramodern',
|
|
4180
3862
|
owners: [
|
|
4181
3863
|
shellApp,
|
|
4182
|
-
...
|
|
3864
|
+
...remotes,
|
|
4183
3865
|
...sharedPackages.map((sharedPackage)=>({
|
|
4184
3866
|
id: sharedPackage.id,
|
|
4185
3867
|
packageSuffix: sharedPackage.id,
|
|
@@ -4206,23 +3888,23 @@ function createOwnership(scope) {
|
|
|
4206
3888
|
}))
|
|
4207
3889
|
};
|
|
4208
3890
|
}
|
|
4209
|
-
function createDevelopmentOverlay() {
|
|
3891
|
+
function createDevelopmentOverlay(remotes = []) {
|
|
4210
3892
|
return {
|
|
4211
3893
|
schemaVersion: 1,
|
|
4212
3894
|
environment: 'development',
|
|
4213
3895
|
preset: 'presetUltramodern',
|
|
4214
3896
|
ports: Object.fromEntries([
|
|
4215
3897
|
shellApp,
|
|
4216
|
-
...
|
|
3898
|
+
...remotes
|
|
4217
3899
|
].map((app)=>[
|
|
4218
3900
|
app.id,
|
|
4219
3901
|
app.port
|
|
4220
3902
|
])),
|
|
4221
|
-
manifests: Object.fromEntries(
|
|
3903
|
+
manifests: Object.fromEntries(remotes.map((remote)=>[
|
|
4222
3904
|
remote.id,
|
|
4223
3905
|
`http://localhost:${remote.port}/mf-manifest.json`
|
|
4224
3906
|
])),
|
|
4225
|
-
apis: Object.fromEntries(verticalEffectApps().map((app)=>[
|
|
3907
|
+
apis: Object.fromEntries(verticalEffectApps(remotes).map((app)=>[
|
|
4226
3908
|
app.id,
|
|
4227
3909
|
`http://localhost:${app.port}${effectApiPrefix(app)}`
|
|
4228
3910
|
]))
|
|
@@ -4258,8 +3940,8 @@ function createPackageSourceMetadata(scope, packageSource) {
|
|
|
4258
3940
|
function createEffectOperationContract(target) {
|
|
4259
3941
|
const stem = effectApiStem(target);
|
|
4260
3942
|
return {
|
|
4261
|
-
group:
|
|
4262
|
-
notFound:
|
|
3943
|
+
group: verticalEffectGroupName(target),
|
|
3944
|
+
notFound: verticalEffectNotFoundErrorExport(target),
|
|
4263
3945
|
operations: {
|
|
4264
3946
|
list: {
|
|
4265
3947
|
method: 'GET',
|
|
@@ -4593,8 +4275,7 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
|
|
|
4593
4275
|
};
|
|
4594
4276
|
}
|
|
4595
4277
|
function createGeneratedContract(scope, apps = [
|
|
4596
|
-
|
|
4597
|
-
...verticalApps
|
|
4278
|
+
createShellHost()
|
|
4598
4279
|
], enableTailwind = true) {
|
|
4599
4280
|
return {
|
|
4600
4281
|
schemaVersion: 1,
|
|
@@ -4626,7 +4307,7 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
4626
4307
|
id: 'modernjs-ultramodern-superapp-workspace',
|
|
4627
4308
|
version: modernVersion,
|
|
4628
4309
|
displayName: 'Modern.js UltraModern SuperApp Workspace',
|
|
4629
|
-
description: '
|
|
4310
|
+
description: 'Growable SuperApp shell, shared packages, and topology skeleton.',
|
|
4630
4311
|
compatibilityLane: 'ultramodern-mv',
|
|
4631
4312
|
minimumModernVersion: modernVersion
|
|
4632
4313
|
},
|
|
@@ -4669,7 +4350,6 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
4669
4350
|
'oxlint.config.ts',
|
|
4670
4351
|
'pnpm-workspace.yaml',
|
|
4671
4352
|
"scripts/**",
|
|
4672
|
-
'services/**',
|
|
4673
4353
|
'topology/**',
|
|
4674
4354
|
'tsconfig.base.json'
|
|
4675
4355
|
],
|
|
@@ -4740,7 +4420,7 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
4740
4420
|
}
|
|
4741
4421
|
};
|
|
4742
4422
|
}
|
|
4743
|
-
function createAssertMfTypesScript(remotes =
|
|
4423
|
+
function createAssertMfTypesScript(remotes = []) {
|
|
4744
4424
|
return `import fs from 'node:fs';
|
|
4745
4425
|
import path from 'node:path';
|
|
4746
4426
|
|
|
@@ -4812,12 +4492,12 @@ for (const appDir of appDirs) {
|
|
|
4812
4492
|
}
|
|
4813
4493
|
`;
|
|
4814
4494
|
}
|
|
4815
|
-
function createWorkspaceValidationScript(scope, enableTailwind, remotes =
|
|
4495
|
+
function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
|
|
4816
4496
|
const verticals = remotes.filter(appHasEffectApi).map((remote)=>({
|
|
4817
4497
|
id: remote.id,
|
|
4818
4498
|
domain: remote.domain,
|
|
4819
4499
|
stem: remote.effectApi.stem,
|
|
4820
|
-
group:
|
|
4500
|
+
group: verticalEffectGroupName(remote),
|
|
4821
4501
|
path: remote.directory,
|
|
4822
4502
|
mfName: remote.mfName,
|
|
4823
4503
|
apiPrefix: remote.effectApi.prefix,
|
|
@@ -4834,6 +4514,9 @@ function createWorkspaceValidationScript(scope, enableTailwind, remotes = vertic
|
|
|
4834
4514
|
const oldRemotePaths = [
|
|
4835
4515
|
'apps/remotes'
|
|
4836
4516
|
];
|
|
4517
|
+
const expectedBuildScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run build && pnpm --filter "./apps/shell-super-app" run build && pnpm ultramodern:assert-mf-types' : 'pnpm --filter "./apps/shell-super-app" run build && pnpm ultramodern:assert-mf-types';
|
|
4518
|
+
const expectedCloudflareBuildScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm ultramodern:assert-mf-types' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm ultramodern:assert-mf-types';
|
|
4519
|
+
const expectedCloudflareDeployScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && pnpm --filter "./apps/shell-super-app" run cloudflare:deploy' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:deploy';
|
|
4837
4520
|
return `import { execFileSync } from 'node:child_process';
|
|
4838
4521
|
import fs from 'node:fs';
|
|
4839
4522
|
import path from 'node:path';
|
|
@@ -4845,6 +4528,9 @@ const tailwindEnabled = ${JSON.stringify(enableTailwind)};
|
|
|
4845
4528
|
const fullStackVerticals = ${JSON.stringify(verticals, null, 2)};
|
|
4846
4529
|
const shellNamespace = ${JSON.stringify(shellNamespace)};
|
|
4847
4530
|
const oldRemotePaths = ${JSON.stringify(oldRemotePaths, null, 2)};
|
|
4531
|
+
const expectedBuildScript = ${JSON.stringify(expectedBuildScript)};
|
|
4532
|
+
const expectedCloudflareBuildScript = ${JSON.stringify(expectedCloudflareBuildScript)};
|
|
4533
|
+
const expectedCloudflareDeployScript = ${JSON.stringify(expectedCloudflareDeployScript)};
|
|
4848
4534
|
|
|
4849
4535
|
const readText = relativePath => fs.readFileSync(path.join(root, relativePath), 'utf-8');
|
|
4850
4536
|
const readJson = relativePath => JSON.parse(readText(relativePath));
|
|
@@ -4900,7 +4586,7 @@ const requiredPaths = [
|
|
|
4900
4586
|
'apps/shell-super-app/module-federation.config.ts',
|
|
4901
4587
|
'apps/shell-super-app/src/modern-app-env.d.ts',
|
|
4902
4588
|
'apps/shell-super-app/src/modern.runtime.ts',
|
|
4903
|
-
'apps/shell-super-app/src/effect/
|
|
4589
|
+
'apps/shell-super-app/src/effect/vertical-clients.ts',
|
|
4904
4590
|
'apps/shell-super-app/locales/en/translation.json',
|
|
4905
4591
|
\`apps/shell-super-app/locales/en/\${shellNamespace}.json\`,
|
|
4906
4592
|
'apps/shell-super-app/locales/cs/translation.json',
|
|
@@ -4956,8 +4642,6 @@ for (const requiredPath of requiredPaths) {
|
|
|
4956
4642
|
for (const oldRemotePath of oldRemotePaths) {
|
|
4957
4643
|
assertNotExists(oldRemotePath);
|
|
4958
4644
|
}
|
|
4959
|
-
assertNotExists('services/service-recommendations-effect');
|
|
4960
|
-
|
|
4961
4645
|
const rootPackage = readJson('package.json');
|
|
4962
4646
|
const packageSource = readJson('.modernjs/ultramodern-package-source.json');
|
|
4963
4647
|
const generatedContract = readJson('.modernjs/ultramodern-generated-contract.json');
|
|
@@ -4973,13 +4657,13 @@ assert(rootPackage.modernjs?.packageSource?.strategy === packageSource.strategy,
|
|
|
4973
4657
|
assert(packageSource.strategy === 'workspace' || packageSource.strategy === 'install', 'Package source strategy must be workspace or install');
|
|
4974
4658
|
assert(packageSource.generatedWorkspacePackages?.specifier === 'workspace:*', 'Generated workspace packages must keep workspace:* links');
|
|
4975
4659
|
assert(
|
|
4976
|
-
rootPackage.scripts?.build ===
|
|
4977
|
-
'pnpm -r --filter "./verticals/*" run build && pnpm --filter "./apps/shell-super-app" run build && pnpm ultramodern:assert-mf-types',
|
|
4660
|
+
rootPackage.scripts?.build === expectedBuildScript,
|
|
4978
4661
|
'Root build script must build verticals before shell',
|
|
4979
4662
|
);
|
|
4663
|
+
assert(rootPackage.scripts?.['cloudflare:build'] === expectedCloudflareBuildScript, 'Root cloudflare:build script is incorrect');
|
|
4980
4664
|
assert(rootPackage.scripts?.['ultramodern:check'] === 'node ./scripts/validate-ultramodern-workspace.mjs', 'Root must expose ultramodern:check');
|
|
4981
4665
|
assert(rootPackage.scripts?.['ultramodern:assert-mf-types'] === 'node ./scripts/assert-mf-types.mjs', 'Root must expose ultramodern:assert-mf-types');
|
|
4982
|
-
assert(rootPackage.scripts?.['cloudflare:deploy']
|
|
4666
|
+
assert(rootPackage.scripts?.['cloudflare:deploy'] === expectedCloudflareDeployScript, 'Root must expose cloudflare:deploy');
|
|
4983
4667
|
assert(rootPackage.scripts?.['cloudflare:proof'] === 'node ./scripts/proof-cloudflare-version.mjs --out .codex/reports/cloudflare-version-proof/public-url-proof.json', 'Root must expose cloudflare:proof');
|
|
4984
4668
|
assert(rootPackage.scripts?.['skills:install'] === 'node ./scripts/bootstrap-agent-skills.mjs', 'Root must expose skills:install');
|
|
4985
4669
|
assert(rootPackage.scripts?.['skills:check'] === 'node ./scripts/bootstrap-agent-skills.mjs --check', 'Root must expose skills:check');
|
|
@@ -4988,7 +4672,7 @@ assert(rootPackage.scripts?.postinstall === 'node ./scripts/bootstrap-agent-skil
|
|
|
4988
4672
|
const expectedAppIds = ['shell-super-app', ...fullStackVerticals.map(vertical => vertical.id)];
|
|
4989
4673
|
assert(
|
|
4990
4674
|
JSON.stringify(generatedContract.apps?.map(app => app.id)) === JSON.stringify(expectedAppIds),
|
|
4991
|
-
'Generated contract must contain shell plus the
|
|
4675
|
+
'Generated contract must contain shell plus the full-stack verticals',
|
|
4992
4676
|
);
|
|
4993
4677
|
assert(generatedContract.cssFederation?.sharedDesignTokens?.owner?.id === 'shared-design-tokens', 'CSS federation must declare shared design token ownership');
|
|
4994
4678
|
assert(generatedContract.cssFederation?.sharedDesignTokens?.role === 'shared-design-tokens', 'CSS federation must mark shared-design-tokens as token owner');
|
|
@@ -5010,7 +4694,7 @@ const expectedZephyrDependencies = Object.fromEntries(
|
|
|
5010
4694
|
assert(
|
|
5011
4695
|
JSON.stringify(shellPackage['zephyr:dependencies']) ===
|
|
5012
4696
|
JSON.stringify(expectedZephyrDependencies),
|
|
5013
|
-
'Shell Zephyr dependencies must reference every
|
|
4697
|
+
'Shell Zephyr dependencies must reference every vertical package',
|
|
5014
4698
|
);
|
|
5015
4699
|
const shellContract = generatedContract.apps?.find(app => app.id === 'shell-super-app');
|
|
5016
4700
|
assert(shellContract?.deploy?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell Cloudflare workerName is incorrect');
|
|
@@ -5028,16 +4712,16 @@ assert(shellContract?.styling?.federation?.dedupe?.duplicateBaseStylesAllowed ==
|
|
|
5028
4712
|
assert(shellContract?.styling?.federation?.ssr?.firstPaintRequired === true, 'Shell CSS must be required for SSR first paint');
|
|
5029
4713
|
assert(
|
|
5030
4714
|
topology.shell?.verticalRefs?.join(',') === fullStackVerticals.map(vertical => vertical.id).join(','),
|
|
5031
|
-
'Topology shell verticalRefs must match
|
|
4715
|
+
'Topology shell verticalRefs must match generated verticals',
|
|
5032
4716
|
);
|
|
5033
|
-
assert(topology.verticals?.length === fullStackVerticals.length, 'Topology must contain only
|
|
4717
|
+
assert(topology.verticals?.length === fullStackVerticals.length, 'Topology must contain only generated verticals');
|
|
5034
4718
|
assert(!('remotes' in topology), 'Topology must not expose legacy remotes; use verticals');
|
|
5035
4719
|
assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, not effectServices');
|
|
5036
4720
|
|
|
5037
4721
|
for (const vertical of fullStackVerticals) {
|
|
5038
4722
|
const packageJson = readJson(\`\${vertical.path}/package.json\`);
|
|
5039
4723
|
assert(packageJson.name === vertical.packageName, \`\${vertical.id} package name is incorrect\`);
|
|
5040
|
-
assert(packageJson.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true
|
|
4724
|
+
assert(packageJson.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy', \`\${vertical.id} must expose cloudflare:deploy\`);
|
|
5041
4725
|
assert(packageJson.scripts?.['cloudflare:proof']?.includes(\`--app \${vertical.id}\`), \`\${vertical.id} must expose cloudflare:proof\`);
|
|
5042
4726
|
assert(packageJson.dependencies?.['@modern-js/plugin-bff'], \`\${vertical.id} must depend on plugin-bff\`);
|
|
5043
4727
|
assert(packageJson.exports?.['./effect/client'] === \`./src/effect/\${vertical.stem}-client.ts\`, \`\${vertical.id} must export its Effect client\`);
|
|
@@ -5169,10 +4853,10 @@ function parseArgs(argv) {
|
|
|
5169
4853
|
|
|
5170
4854
|
function printHelp() {
|
|
5171
4855
|
process.stdout.write(\`Usage:
|
|
5172
|
-
node scripts/proof-cloudflare-version.mjs [--app
|
|
4856
|
+
node scripts/proof-cloudflare-version.mjs [--app workspace] [--out evidence.json] [--require-public-urls]
|
|
5173
4857
|
|
|
5174
4858
|
Set each app's public URL using the contract env key, for example:
|
|
5175
|
-
|
|
4859
|
+
ULTRAMODERN_PUBLIC_URL_WORKSPACE=https://workspace.example.workers.dev
|
|
5176
4860
|
\`);
|
|
5177
4861
|
}
|
|
5178
4862
|
|
|
@@ -5437,64 +5121,56 @@ main().then(
|
|
|
5437
5121
|
);
|
|
5438
5122
|
`;
|
|
5439
5123
|
}
|
|
5440
|
-
function writeGeneratedWorkspaceScripts(targetDir, scope, enableTailwind, remotes =
|
|
5441
|
-
writeFileReplacing(targetDir, "scripts/assert-mf-types.mjs", createAssertMfTypesScript());
|
|
5124
|
+
function writeGeneratedWorkspaceScripts(targetDir, scope, enableTailwind, remotes = []) {
|
|
5125
|
+
writeFileReplacing(targetDir, "scripts/assert-mf-types.mjs", createAssertMfTypesScript(remotes));
|
|
5442
5126
|
writeFileReplacing(targetDir, "scripts/validate-ultramodern-workspace.mjs", createWorkspaceValidationScript(scope, enableTailwind, remotes));
|
|
5443
5127
|
writeFileReplacing(targetDir, "scripts/proof-cloudflare-version.mjs", createCloudflareVersionProofScript());
|
|
5444
5128
|
}
|
|
5445
|
-
function writeApp(targetDir, scope, app, packageSource, enableTailwind) {
|
|
5129
|
+
function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes = []) {
|
|
5130
|
+
const resolvedApp = 'shell' === app.kind ? createShellHost(remotes) : app;
|
|
5446
5131
|
const writeAppFile = (relativePath, content)=>{
|
|
5447
|
-
writeFile(targetDir, `${
|
|
5132
|
+
writeFile(targetDir, `${resolvedApp.directory}/${relativePath}`, content);
|
|
5448
5133
|
};
|
|
5449
|
-
writeJson(targetDir, `${
|
|
5450
|
-
writeJson(targetDir, `${
|
|
5451
|
-
writeFile(targetDir, `${
|
|
5452
|
-
writeFile(targetDir, `${
|
|
5453
|
-
writeFile(targetDir, `${
|
|
5454
|
-
writeFile(targetDir, `${
|
|
5455
|
-
writeFile(targetDir, `${
|
|
5456
|
-
writeJson(targetDir, `${
|
|
5457
|
-
writeJson(targetDir, `${
|
|
5458
|
-
writeJson(targetDir, `${
|
|
5459
|
-
writeJson(targetDir, `${
|
|
5460
|
-
writeFile(targetDir, `${
|
|
5134
|
+
writeJson(targetDir, `${resolvedApp.directory}/package.json`, createAppPackage(scope, resolvedApp, packageSource, enableTailwind, remotes));
|
|
5135
|
+
writeJson(targetDir, `${resolvedApp.directory}/tsconfig.json`, createPackageTsConfig(resolvedApp.directory, appHasEffectApi(resolvedApp)));
|
|
5136
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(resolvedApp, remotes));
|
|
5137
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/ultramodern-build.ts`, createUltramodernBuildModule(scope, resolvedApp));
|
|
5138
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/routes/ultramodern-route-metadata.ts`, createRouteMetadataModule(resolvedApp));
|
|
5139
|
+
writeFile(targetDir, `${resolvedApp.directory}/modern.config.ts`, createAppModernConfig(scope, resolvedApp));
|
|
5140
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(resolvedApp, scope, remotes));
|
|
5141
|
+
writeJson(targetDir, `${resolvedApp.directory}/locales/en/translation.json`, createAppPublicLocaleMessages(resolvedApp, 'en', remotes));
|
|
5142
|
+
writeJson(targetDir, `${resolvedApp.directory}/locales/en/${appI18nNamespace(resolvedApp)}.json`, createAppPublicLocaleMessages(resolvedApp, 'en', remotes));
|
|
5143
|
+
writeJson(targetDir, `${resolvedApp.directory}/locales/cs/translation.json`, createAppPublicLocaleMessages(resolvedApp, 'cs', remotes));
|
|
5144
|
+
writeJson(targetDir, `${resolvedApp.directory}/locales/cs/${appI18nNamespace(resolvedApp)}.json`, createAppPublicLocaleMessages(resolvedApp, 'cs', remotes));
|
|
5145
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/routes/index.css`, createAppStyles(enableTailwind, scope, resolvedApp));
|
|
5461
5146
|
if (enableTailwind) {
|
|
5462
|
-
writeFile(targetDir, `${
|
|
5463
|
-
writeFile(targetDir, `${
|
|
5147
|
+
writeFile(targetDir, `${resolvedApp.directory}/postcss.config.mjs`, createPostcssConfig());
|
|
5148
|
+
writeFile(targetDir, `${resolvedApp.directory}/tailwind.config.ts`, createTailwindConfig());
|
|
5464
5149
|
}
|
|
5465
|
-
writeFile(targetDir, `${
|
|
5466
|
-
writeAppFile('src/routes/layout.tsx', createLayout(
|
|
5467
|
-
for (const [relativePath, content] of Object.entries(
|
|
5468
|
-
writeAppFile('src/routes/[lang]/page.tsx', 'shell' ===
|
|
5469
|
-
for (const route of createRouteOwnedI18nPaths(
|
|
5470
|
-
if ('shell' ===
|
|
5471
|
-
writeAppFile('src/routes/vertical-components.tsx', createShellRemoteComponents(scope));
|
|
5150
|
+
writeFile(targetDir, `${resolvedApp.directory}/module-federation.config.ts`, 'shell' === resolvedApp.kind ? createShellModuleFederationConfig(scope, remotes) : createRemoteModuleFederationConfig(scope, resolvedApp, remotes));
|
|
5151
|
+
writeAppFile('src/routes/layout.tsx', createLayout(resolvedApp.id));
|
|
5152
|
+
for (const [relativePath, content] of Object.entries(workspaceAssetsForApp(resolvedApp)))writeFile(targetDir, `${resolvedApp.directory}/${relativePath}`, content);
|
|
5153
|
+
writeAppFile('src/routes/[lang]/page.tsx', 'shell' === resolvedApp.kind ? createShellPage(remotes) : createRemotePage(resolvedApp));
|
|
5154
|
+
for (const route of createRouteOwnedI18nPaths(resolvedApp))if ('/' !== route.canonicalPath && 'shell' !== resolvedApp.kind) writeFile(targetDir, createRoutePageFilePath(resolvedApp, route.canonicalPath), createRouteAliasPage(route.canonicalPath));
|
|
5155
|
+
if ('shell' === resolvedApp.kind) {
|
|
5156
|
+
writeAppFile('src/routes/vertical-components.tsx', createShellRemoteComponents(scope, remotes));
|
|
5472
5157
|
writeAppFile('src/routes/shell-frame.tsx', createShellFrameComponent());
|
|
5473
|
-
|
|
5474
|
-
writeFile(targetDir, `${app.directory}/src/effect/recommendations-client.ts`, createShellEffectClient(scope));
|
|
5475
|
-
writeAppFile('src/routes/[lang]/tractors/page.tsx', createShellTractorsPage());
|
|
5476
|
-
writeAppFile('src/routes/[lang]/stores/page.tsx', createShellStoresPage());
|
|
5477
|
-
writeAppFile('src/routes/[lang]/tractors/[slug]/page.tsx', createShellProductPage());
|
|
5478
|
-
writeAppFile('src/routes/[lang]/cart/page.tsx', createShellCartPage());
|
|
5158
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/effect/vertical-clients.ts`, createShellEffectClient(scope, remotes));
|
|
5479
5159
|
}
|
|
5480
|
-
if (appHasEffectApi(
|
|
5481
|
-
writeFile(targetDir, `${
|
|
5482
|
-
writeFile(targetDir, `${
|
|
5483
|
-
writeFile(targetDir, `${
|
|
5160
|
+
if (appHasEffectApi(resolvedApp)) {
|
|
5161
|
+
writeFile(targetDir, `${resolvedApp.directory}/shared/effect/api.ts`, createEffectSharedApi(resolvedApp));
|
|
5162
|
+
writeFile(targetDir, `${resolvedApp.directory}/api/effect/index.ts`, createEffectServiceEntry(scope, resolvedApp, '../../shared/effect/api'));
|
|
5163
|
+
writeFile(targetDir, `${resolvedApp.directory}/src/effect/${resolvedApp.effectApi.stem}-client.ts`, createEffectClient(resolvedApp, '../../shared/effect/api'));
|
|
5484
5164
|
}
|
|
5485
|
-
if ('vertical' ===
|
|
5486
|
-
writeAppFile('src/federation-entry.tsx', createRemoteEntry(
|
|
5487
|
-
if ('
|
|
5488
|
-
if ('
|
|
5489
|
-
for (const expose of Object.keys(
|
|
5490
|
-
const outputPath = remoteComponentOutputPath(
|
|
5491
|
-
if (outputPath) writeAppFile(outputPath.slice(
|
|
5165
|
+
if ('vertical' === resolvedApp.kind) {
|
|
5166
|
+
writeAppFile('src/federation-entry.tsx', createRemoteEntry(resolvedApp));
|
|
5167
|
+
if ('records' === resolvedApp.id) writeAppFile('src/components/vertical-components.tsx', createRecordsRemoteComponents(scope, resolvedApp));
|
|
5168
|
+
if ('actions' === resolvedApp.id) writeFile(targetDir, `${resolvedApp.directory}/src/action-queue-store.ts`, createActionQueueStore());
|
|
5169
|
+
for (const expose of Object.keys(resolvedApp.exposes ?? {})){
|
|
5170
|
+
const outputPath = remoteComponentOutputPath(resolvedApp, expose);
|
|
5171
|
+
if (outputPath) writeAppFile(outputPath.slice(resolvedApp.directory.length + 1), createRemoteExposeComponent(resolvedApp, expose));
|
|
5492
5172
|
}
|
|
5493
5173
|
}
|
|
5494
|
-
if ('horizontal-design-system' === app.kind) {
|
|
5495
|
-
writeAppFile('src/components/button.tsx', createDesignButton(app));
|
|
5496
|
-
writeFile(targetDir, `${app.directory}/src/tokens.ts`, createDesignTokens());
|
|
5497
|
-
}
|
|
5498
5174
|
}
|
|
5499
5175
|
function writeSharedPackages(targetDir, scope, packageSource) {
|
|
5500
5176
|
for (const sharedPackage of sharedPackages){
|
|
@@ -5578,17 +5254,29 @@ function nextAvailablePort(ports) {
|
|
|
5578
5254
|
function assertCanCreate(workspaceRoot, relativePath) {
|
|
5579
5255
|
if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
|
|
5580
5256
|
}
|
|
5581
|
-
function
|
|
5257
|
+
function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes) {
|
|
5582
5258
|
const packagePath = node_path.join(workspaceRoot, 'package.json');
|
|
5583
5259
|
const rootPackage = readJsonFile(packagePath);
|
|
5584
|
-
|
|
5585
|
-
rootPackage.scripts
|
|
5586
|
-
if ('string' == typeof rootPackage.scripts.dev && !rootPackage.scripts.dev.includes(ultramodern_workspace_packageName(scope, packageSuffix))) {
|
|
5587
|
-
const packageFilter = `--filter ${ultramodern_workspace_packageName(scope, packageSuffix)}`;
|
|
5588
|
-
rootPackage.scripts.dev = rootPackage.scripts.dev.endsWith(' dev') ? rootPackage.scripts.dev.replace(/ dev$/u, ` ${packageFilter} dev`) : `${rootPackage.scripts.dev} ${packageFilter}`;
|
|
5589
|
-
}
|
|
5260
|
+
const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes);
|
|
5261
|
+
rootPackage.scripts = generatedRootPackage.scripts;
|
|
5590
5262
|
writeJsonFile(packagePath, rootPackage);
|
|
5591
5263
|
}
|
|
5264
|
+
function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
|
|
5265
|
+
const shellHost = createShellHost(remotes);
|
|
5266
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
|
|
5267
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
|
|
5268
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/ultramodern-route-metadata.ts`, createRouteMetadataModule(shellHost));
|
|
5269
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(shellHost, scope, remotes));
|
|
5270
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/translation.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));
|
|
5271
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/${appI18nNamespace(shellHost)}.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));
|
|
5272
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/cs/translation.json`), createAppPublicLocaleMessages(shellHost, 'cs', remotes));
|
|
5273
|
+
writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/cs/${appI18nNamespace(shellHost)}.json`), createAppPublicLocaleMessages(shellHost, 'cs', remotes));
|
|
5274
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/module-federation.config.ts`, createShellModuleFederationConfig(scope, remotes));
|
|
5275
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/[lang]/page.tsx`, createShellPage(remotes));
|
|
5276
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/vertical-components.tsx`, createShellRemoteComponents(scope, remotes));
|
|
5277
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/shell-frame.tsx`, createShellFrameComponent());
|
|
5278
|
+
writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/effect/vertical-clients.ts`, createShellEffectClient(scope, remotes));
|
|
5279
|
+
}
|
|
5592
5280
|
function addShellZephyrDependency(workspaceRoot, scope, remote) {
|
|
5593
5281
|
const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
|
|
5594
5282
|
const shellPackage = readJsonFile(packagePath);
|
|
@@ -5604,7 +5292,7 @@ function addShellWorkspaceDependency(workspaceRoot, scope, remote) {
|
|
|
5604
5292
|
shellPackage.dependencies[ultramodern_workspace_packageName(scope, remote.packageSuffix)] = WORKSPACE_PACKAGE_VERSION;
|
|
5605
5293
|
writeJsonFile(packagePath, shellPackage);
|
|
5606
5294
|
}
|
|
5607
|
-
function verticalTopologyEntry(scope, vertical) {
|
|
5295
|
+
function verticalTopologyEntry(scope, vertical, remotes = []) {
|
|
5608
5296
|
return {
|
|
5609
5297
|
id: vertical.id,
|
|
5610
5298
|
kind: vertical.kind,
|
|
@@ -5618,7 +5306,7 @@ function verticalTopologyEntry(scope, vertical) {
|
|
|
5618
5306
|
exposes: Object.keys(vertical.exposes ?? {}),
|
|
5619
5307
|
...vertical.verticalRefs?.length ? {
|
|
5620
5308
|
verticalRefs: vertical.verticalRefs,
|
|
5621
|
-
remotes: createModuleFederationRemoteContracts(vertical)
|
|
5309
|
+
remotes: createModuleFederationRemoteContracts(vertical, remotes)
|
|
5622
5310
|
} : {},
|
|
5623
5311
|
ssr: true,
|
|
5624
5312
|
fallbackTelemetryEvent: 'modernjs:mv-runtime-parity',
|
|
@@ -5658,13 +5346,13 @@ function verticalsFromTopology(topology, ports) {
|
|
|
5658
5346
|
displayName: vertical.displayName ?? `${toPascalCase(domain)} Vertical`,
|
|
5659
5347
|
kind: 'vertical',
|
|
5660
5348
|
domain,
|
|
5661
|
-
portEnv:
|
|
5349
|
+
portEnv: `VERTICAL_${toEnvSegment(domain)}_PORT`,
|
|
5662
5350
|
port: 'number' == typeof ports[vertical.id] ? ports[vertical.id] : 0,
|
|
5663
5351
|
mfName: vertical.moduleFederation?.name ?? `vertical${toPascalCase(domain)}`,
|
|
5664
5352
|
...Array.isArray(vertical.moduleFederation?.exposes) ? {
|
|
5665
5353
|
exposes: Object.fromEntries(vertical.moduleFederation.exposes.map((expose)=>[
|
|
5666
5354
|
expose,
|
|
5667
|
-
''
|
|
5355
|
+
'./Route' === expose ? './src/federation-entry.tsx' : './Widget' === expose ? `./src/components/${domain}-widget.tsx` : ''
|
|
5668
5356
|
]))
|
|
5669
5357
|
} : {},
|
|
5670
5358
|
...Array.isArray(vertical.moduleFederation?.verticalRefs) ? {
|
|
@@ -5737,21 +5425,20 @@ function addUltramodernVertical(options) {
|
|
|
5737
5425
|
},
|
|
5738
5426
|
...updatedVerticals
|
|
5739
5427
|
], enableTailwind));
|
|
5740
|
-
|
|
5741
|
-
writeFileReplacing(options.workspaceRoot, `${shellApp.directory}/module-federation.config.ts`, createShellModuleFederationConfig(scope, updatedVerticals));
|
|
5742
|
-
if (!node_fs.existsSync(shellConfigPath)) throw new Error('Shell Module Federation config was not regenerated');
|
|
5428
|
+
rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals);
|
|
5743
5429
|
writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
|
|
5744
5430
|
addShellZephyrDependency(options.workspaceRoot, scope, vertical);
|
|
5745
5431
|
addShellWorkspaceDependency(options.workspaceRoot, scope, vertical);
|
|
5746
|
-
|
|
5432
|
+
updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals);
|
|
5747
5433
|
}
|
|
5748
5434
|
function generateUltramodernWorkspace(options) {
|
|
5749
5435
|
const scope = toPackageScope(options.packageName);
|
|
5750
5436
|
const packageSource = resolvePackageSource(options);
|
|
5751
5437
|
const enableTailwind = false !== options.enableTailwind;
|
|
5438
|
+
const initialVerticals = [];
|
|
5752
5439
|
assertUniqueTailwindPrefixes([
|
|
5753
5440
|
shellApp,
|
|
5754
|
-
...
|
|
5441
|
+
...initialVerticals
|
|
5755
5442
|
]);
|
|
5756
5443
|
node_fs.mkdirSync(options.targetDir, {
|
|
5757
5444
|
recursive: true
|
|
@@ -5762,21 +5449,21 @@ function generateUltramodernWorkspace(options) {
|
|
|
5762
5449
|
pnpmVersion: PNPM_VERSION,
|
|
5763
5450
|
tailwindEnabled: String(enableTailwind)
|
|
5764
5451
|
});
|
|
5765
|
-
writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource));
|
|
5452
|
+
writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource, initialVerticals));
|
|
5766
5453
|
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase());
|
|
5767
|
-
writeJson(options.targetDir, 'topology/reference-topology.json', createTopology(scope));
|
|
5768
|
-
writeJson(options.targetDir, 'topology/ownership.json', createOwnership(scope));
|
|
5769
|
-
writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay());
|
|
5454
|
+
writeJson(options.targetDir, 'topology/reference-topology.json', createTopology(scope, initialVerticals));
|
|
5455
|
+
writeJson(options.targetDir, 'topology/ownership.json', createOwnership(scope, initialVerticals));
|
|
5456
|
+
writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay(initialVerticals));
|
|
5770
5457
|
writeJson(options.targetDir, '.modernjs/ultramodern-workspace-template-manifest.json', createTemplateManifest(options.modernVersion, packageSource));
|
|
5771
5458
|
writeJson(options.targetDir, '.modernjs/ultramodern-package-source.json', createPackageSourceMetadata(scope, packageSource));
|
|
5772
5459
|
writeJson(options.targetDir, GENERATED_CONTRACT_PATH, createGeneratedContract(scope, [
|
|
5773
|
-
|
|
5774
|
-
...
|
|
5460
|
+
createShellHost(initialVerticals),
|
|
5461
|
+
...initialVerticals
|
|
5775
5462
|
], enableTailwind));
|
|
5776
|
-
writeApp(options.targetDir, scope, shellApp, packageSource, enableTailwind);
|
|
5777
|
-
for (const remote of
|
|
5463
|
+
writeApp(options.targetDir, scope, shellApp, packageSource, enableTailwind, initialVerticals);
|
|
5464
|
+
for (const remote of initialVerticals)writeApp(options.targetDir, scope, remote, packageSource, enableTailwind, initialVerticals);
|
|
5778
5465
|
writeSharedPackages(options.targetDir, scope, packageSource);
|
|
5779
|
-
writeGeneratedWorkspaceScripts(options.targetDir, scope, enableTailwind);
|
|
5466
|
+
writeGeneratedWorkspaceScripts(options.targetDir, scope, enableTailwind, initialVerticals);
|
|
5780
5467
|
}
|
|
5781
5468
|
const src_dirname = node_path.dirname(fileURLToPath(import.meta.url));
|
|
5782
5469
|
const templateDir = node_path.resolve(src_dirname, '..', 'template');
|
|
@@ -6196,6 +5883,7 @@ function showHelp() {
|
|
|
6196
5883
|
if (localeKeys.help.example9) console.log(i18n.t(localeKeys.help.example9));
|
|
6197
5884
|
if (localeKeys.help.example10) console.log(i18n.t(localeKeys.help.example10));
|
|
6198
5885
|
if (localeKeys.help.example11) console.log(i18n.t(localeKeys.help.example11));
|
|
5886
|
+
if (localeKeys.help.example12) console.log(i18n.t(localeKeys.help.example12));
|
|
6199
5887
|
console.log('');
|
|
6200
5888
|
console.log(i18n.t(localeKeys.help.moreInfo));
|
|
6201
5889
|
console.log('');
|
|
@@ -6240,9 +5928,9 @@ function detectVerticalFlag() {
|
|
|
6240
5928
|
}
|
|
6241
5929
|
return args.includes('--vertical');
|
|
6242
5930
|
}
|
|
6243
|
-
function detectUltramodernWorkspaceFlag(
|
|
5931
|
+
function detectUltramodernWorkspaceFlag() {
|
|
6244
5932
|
const args = process.argv.slice(2);
|
|
6245
|
-
return args.includes(ULTRAMODERN_WORKSPACE_FLAG)
|
|
5933
|
+
return args.includes(ULTRAMODERN_WORKSPACE_FLAG);
|
|
6246
5934
|
}
|
|
6247
5935
|
function detectUltramodernPackageSource(args, defaultPackageVersion, createPackage) {
|
|
6248
5936
|
const bleedingDevDefaults = isBleedingDevCreatePackage(createPackage);
|
|
@@ -6429,7 +6117,7 @@ async function main() {
|
|
|
6429
6117
|
process.exit(1);
|
|
6430
6118
|
}
|
|
6431
6119
|
}
|
|
6432
|
-
const generateWorkspace = detectUltramodernWorkspaceFlag(
|
|
6120
|
+
const generateWorkspace = detectUltramodernWorkspaceFlag();
|
|
6433
6121
|
if (generateWorkspace) {
|
|
6434
6122
|
generateUltramodernWorkspace({
|
|
6435
6123
|
targetDir,
|