@bleedingdev/modern-js-create 3.2.0-ultramodern.10 → 3.2.0-ultramodern.12
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 +249 -146
- package/package.json +3 -3
- package/template/AGENTS.md +5 -0
- package/template/config/public/locales/cs/translation.json +39 -0
- package/template/config/public/locales/en/translation.json +39 -0
- package/template/modern.config.ts.handlebars +8 -1
- package/template/package.json.handlebars +8 -3
- package/template/scripts/check-i18n-strings.mjs +83 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +12 -0
- package/template/src/modern.runtime.ts.handlebars +17 -1
- package/template/src/routes/page.tsx.handlebars +45 -26
- package/template-workspace/AGENTS.md +6 -1
- package/template-workspace/oxfmt.config.ts +12 -3
- package/template-workspace/oxlint.config.ts +11 -4
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +17 -28
- package/template-workspace/scripts/check-i18n-strings.mjs +83 -0
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +121 -91
package/dist/index.js
CHANGED
|
@@ -561,8 +561,10 @@ const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260518.1';
|
|
|
561
561
|
const OXLINT_VERSION = '1.65.0';
|
|
562
562
|
const OXFMT_VERSION = '0.50.0';
|
|
563
563
|
const ULTRACITE_VERSION = '7.7.0';
|
|
564
|
+
const I18NEXT_VERSION = '26.2.0';
|
|
564
565
|
const REACT_VERSION = '^19.2.6';
|
|
565
566
|
const REACT_DOM_VERSION = '^19.2.6';
|
|
567
|
+
const REACT_I18NEXT_VERSION = '17.0.8';
|
|
566
568
|
const WORKSPACE_PACKAGE_VERSION = 'workspace:*';
|
|
567
569
|
const RSTACK_AGENT_SKILLS_COMMIT = '61c948b42512e223bad44b83af4080eba48b2677';
|
|
568
570
|
const baselineAgentSkills = [
|
|
@@ -585,6 +587,7 @@ const effectTsgoTypecheckCommand = "node -e \"const fs = require('node:fs'); con
|
|
|
585
587
|
const modernPackageNames = [
|
|
586
588
|
'@modern-js/app-tools',
|
|
587
589
|
'@modern-js/plugin-bff',
|
|
590
|
+
'@modern-js/plugin-i18n',
|
|
588
591
|
'@modern-js/plugin-tanstack',
|
|
589
592
|
'@modern-js/runtime'
|
|
590
593
|
];
|
|
@@ -631,7 +634,7 @@ const remoteApps = [
|
|
|
631
634
|
mfName: 'remoteCommerce',
|
|
632
635
|
exposes: {
|
|
633
636
|
'./Route': './src/remote-entry.tsx',
|
|
634
|
-
'./Widget': './src/components/
|
|
637
|
+
'./Widget': './src/components/commerce-widget.tsx'
|
|
635
638
|
},
|
|
636
639
|
ownership: {
|
|
637
640
|
team: 'commerce-experience',
|
|
@@ -660,7 +663,7 @@ const remoteApps = [
|
|
|
660
663
|
mfName: 'remoteIdentity',
|
|
661
664
|
exposes: {
|
|
662
665
|
'./Route': './src/remote-entry.tsx',
|
|
663
|
-
'./Widget': './src/components/
|
|
666
|
+
'./Widget': './src/components/identity-widget.tsx'
|
|
664
667
|
},
|
|
665
668
|
ownership: {
|
|
666
669
|
team: 'identity-platform',
|
|
@@ -688,7 +691,7 @@ const remoteApps = [
|
|
|
688
691
|
port: 3023,
|
|
689
692
|
mfName: 'remoteDesignSystem',
|
|
690
693
|
exposes: {
|
|
691
|
-
'./Button': './src/components/
|
|
694
|
+
'./Button': './src/components/button.tsx',
|
|
692
695
|
'./tokens': './src/tokens.ts'
|
|
693
696
|
},
|
|
694
697
|
ownership: {
|
|
@@ -917,6 +920,7 @@ function modernPackageSpecifier(packageName, packageSource) {
|
|
|
917
920
|
}
|
|
918
921
|
function appDependencies(scope, packageSource) {
|
|
919
922
|
return {
|
|
923
|
+
'@modern-js/plugin-i18n': modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
|
|
920
924
|
'@modern-js/plugin-tanstack': modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
|
|
921
925
|
'@modern-js/runtime': modernPackageSpecifier('@modern-js/runtime', packageSource),
|
|
922
926
|
'@module-federation/modern-js-v3': MODULE_FEDERATION_VERSION,
|
|
@@ -924,8 +928,10 @@ function appDependencies(scope, packageSource) {
|
|
|
924
928
|
'@tanstack/react-router': TANSTACK_ROUTER_VERSION,
|
|
925
929
|
[ultramodern_workspace_packageName(scope, 'shared-contracts')]: WORKSPACE_PACKAGE_VERSION,
|
|
926
930
|
[ultramodern_workspace_packageName(scope, 'shared-design-tokens')]: WORKSPACE_PACKAGE_VERSION,
|
|
931
|
+
i18next: I18NEXT_VERSION,
|
|
927
932
|
react: REACT_VERSION,
|
|
928
|
-
'react-dom': REACT_DOM_VERSION
|
|
933
|
+
'react-dom': REACT_DOM_VERSION,
|
|
934
|
+
'react-i18next': REACT_I18NEXT_VERSION
|
|
929
935
|
};
|
|
930
936
|
}
|
|
931
937
|
function appDevDependencies(packageSource) {
|
|
@@ -943,6 +949,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
943
949
|
private: true,
|
|
944
950
|
name: scope,
|
|
945
951
|
version: '0.1.0',
|
|
952
|
+
type: 'module',
|
|
946
953
|
packageManager: 'pnpm@11.1.2',
|
|
947
954
|
scripts: {
|
|
948
955
|
dev: `pnpm --parallel --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} --filter ${ultramodern_workspace_packageName(scope, 'remote-commerce')} --filter ${ultramodern_workspace_packageName(scope, 'remote-identity')} --filter ${ultramodern_workspace_packageName(scope, 'remote-design-system')} dev`,
|
|
@@ -954,13 +961,14 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
954
961
|
build: 'pnpm -r --filter ./apps/** --filter ./services/** build',
|
|
955
962
|
format: 'oxfmt .',
|
|
956
963
|
'format:check': 'oxfmt --check .',
|
|
964
|
+
'i18n:check': "node ./scripts/check-i18n-strings.mjs",
|
|
957
965
|
lint: 'oxlint .',
|
|
958
966
|
'lint:fix': 'oxlint . --fix',
|
|
959
|
-
typecheck:
|
|
967
|
+
typecheck: `pnpm -r --filter "@${scope}/*" typecheck`,
|
|
960
968
|
'skills:install': "node ./scripts/bootstrap-agent-skills.mjs",
|
|
961
969
|
'skills:check': "node ./scripts/bootstrap-agent-skills.mjs --check",
|
|
962
970
|
'ultramodern:check': "node ./scripts/validate-ultramodern-workspace.mjs",
|
|
963
|
-
check: 'pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm ultramodern:check'
|
|
971
|
+
check: 'pnpm format:check && pnpm lint && pnpm typecheck && pnpm i18n:check && pnpm skills:check && pnpm ultramodern:check'
|
|
964
972
|
},
|
|
965
973
|
engines: {
|
|
966
974
|
node: '>=20',
|
|
@@ -991,7 +999,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
991
999
|
}
|
|
992
1000
|
};
|
|
993
1001
|
}
|
|
994
|
-
function createTsConfigBase(
|
|
1002
|
+
function createTsConfigBase() {
|
|
995
1003
|
return {
|
|
996
1004
|
compilerOptions: {
|
|
997
1005
|
target: 'ESNext',
|
|
@@ -1018,12 +1026,6 @@ function createTsConfigBase(scope) {
|
|
|
1018
1026
|
noImplicitReturns: true,
|
|
1019
1027
|
skipLibCheck: true,
|
|
1020
1028
|
resolveJsonModule: true,
|
|
1021
|
-
paths: Object.fromEntries(sharedPackages.map((sharedPackage)=>[
|
|
1022
|
-
ultramodern_workspace_packageName(scope, sharedPackage.id),
|
|
1023
|
-
[
|
|
1024
|
-
`${sharedPackage.directory}/src/index.ts`
|
|
1025
|
-
]
|
|
1026
|
-
])),
|
|
1027
1029
|
plugins: [
|
|
1028
1030
|
{
|
|
1029
1031
|
name: '@effect/language-service',
|
|
@@ -1051,20 +1053,6 @@ function createPackageTsConfig(packageDir, includeApi = false) {
|
|
|
1051
1053
|
if (includeApi) include.push('api', 'shared');
|
|
1052
1054
|
return {
|
|
1053
1055
|
extends: `${relativeRootFor(packageDir)}/tsconfig.base.json`,
|
|
1054
|
-
compilerOptions: {
|
|
1055
|
-
baseUrl: '.',
|
|
1056
|
-
paths: {
|
|
1057
|
-
'@/*': [
|
|
1058
|
-
'./src/*'
|
|
1059
|
-
],
|
|
1060
|
-
'@api/*': [
|
|
1061
|
-
'./api/*'
|
|
1062
|
-
],
|
|
1063
|
-
'@shared/*': [
|
|
1064
|
-
'./shared/*'
|
|
1065
|
-
]
|
|
1066
|
-
}
|
|
1067
|
-
},
|
|
1068
1056
|
include
|
|
1069
1057
|
};
|
|
1070
1058
|
}
|
|
@@ -1145,6 +1133,7 @@ function createSharedPackage(scope, id, description) {
|
|
|
1145
1133
|
function createAppModernConfig(app) {
|
|
1146
1134
|
return `// @effect-diagnostics processEnv:off
|
|
1147
1135
|
import { appTools, defineConfig, presetUltramodern } from '@modern-js/app-tools';
|
|
1136
|
+
import { i18nPlugin } from '@modern-js/plugin-i18n';
|
|
1148
1137
|
import { tanstackRouterPlugin } from '@modern-js/plugin-tanstack';
|
|
1149
1138
|
import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
|
|
1150
1139
|
|
|
@@ -1154,28 +1143,34 @@ const port = Number(process.env['${app.portEnv}'] ?? ${app.port});
|
|
|
1154
1143
|
export default defineConfig(
|
|
1155
1144
|
presetUltramodern(
|
|
1156
1145
|
{
|
|
1157
|
-
server: {
|
|
1158
|
-
port,
|
|
1159
|
-
ssr: {
|
|
1160
|
-
mode: 'string',
|
|
1161
|
-
moduleFederationAppSSR: true,
|
|
1162
|
-
},
|
|
1163
|
-
},
|
|
1164
1146
|
output: {
|
|
1165
|
-
polyfill: 'off',
|
|
1166
1147
|
disableTsChecker: true,
|
|
1148
|
+
polyfill: 'off',
|
|
1167
1149
|
splitRouteChunks: false,
|
|
1168
1150
|
},
|
|
1169
1151
|
plugins: [
|
|
1170
1152
|
appTools(),
|
|
1153
|
+
i18nPlugin({
|
|
1154
|
+
localeDetection: {
|
|
1155
|
+
fallbackLanguage: 'en',
|
|
1156
|
+
languages: ['en', 'cs'],
|
|
1157
|
+
},
|
|
1158
|
+
}),
|
|
1171
1159
|
tanstackRouterPlugin(),
|
|
1172
1160
|
moduleFederationPlugin(),
|
|
1173
1161
|
],
|
|
1162
|
+
server: {
|
|
1163
|
+
port,
|
|
1164
|
+
ssr: {
|
|
1165
|
+
mode: 'string',
|
|
1166
|
+
moduleFederationAppSSR: true,
|
|
1167
|
+
},
|
|
1168
|
+
},
|
|
1174
1169
|
},
|
|
1175
1170
|
{
|
|
1176
1171
|
appId,
|
|
1177
|
-
enableModuleFederationSSR: true,
|
|
1178
1172
|
enableBffRequestId: true,
|
|
1173
|
+
enableModuleFederationSSR: true,
|
|
1179
1174
|
enableTelemetryExporters: true,
|
|
1180
1175
|
telemetryFailLoudStartup: false,
|
|
1181
1176
|
},
|
|
@@ -1183,6 +1178,47 @@ export default defineConfig(
|
|
|
1183
1178
|
);
|
|
1184
1179
|
`;
|
|
1185
1180
|
}
|
|
1181
|
+
function createSharedModuleFederationConfig() {
|
|
1182
|
+
return ` shared: {
|
|
1183
|
+
'@modern-js/runtime': {
|
|
1184
|
+
requiredVersion: runtimeVersion,
|
|
1185
|
+
singleton: true,
|
|
1186
|
+
treeShaking: false,
|
|
1187
|
+
},
|
|
1188
|
+
'@tanstack/react-router': {
|
|
1189
|
+
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1190
|
+
singleton: true,
|
|
1191
|
+
treeShaking: false,
|
|
1192
|
+
},
|
|
1193
|
+
i18next: {
|
|
1194
|
+
requiredVersion: dependencies.i18next,
|
|
1195
|
+
singleton: true,
|
|
1196
|
+
treeShaking: false,
|
|
1197
|
+
},
|
|
1198
|
+
react: {
|
|
1199
|
+
requiredVersion: reactVersion,
|
|
1200
|
+
singleton: true,
|
|
1201
|
+
treeShaking: false,
|
|
1202
|
+
},
|
|
1203
|
+
'react-dom': {
|
|
1204
|
+
requiredVersion: reactDomVersion,
|
|
1205
|
+
singleton: true,
|
|
1206
|
+
treeShaking: false,
|
|
1207
|
+
},
|
|
1208
|
+
'react-i18next': {
|
|
1209
|
+
requiredVersion: dependencies['react-i18next'],
|
|
1210
|
+
singleton: true,
|
|
1211
|
+
treeShaking: false,
|
|
1212
|
+
},
|
|
1213
|
+
}`;
|
|
1214
|
+
}
|
|
1215
|
+
function formatTsObjectLiteral(value) {
|
|
1216
|
+
const entries = Object.entries(value).sort(([left], [right])=>left.localeCompare(right));
|
|
1217
|
+
if (0 === entries.length) return '{}';
|
|
1218
|
+
return `{
|
|
1219
|
+
${entries.map(([key, entryValue])=>` '${key}': '${entryValue}',`).join('\n')}
|
|
1220
|
+
}`;
|
|
1221
|
+
}
|
|
1186
1222
|
function createShellModuleFederationConfig() {
|
|
1187
1223
|
return `// @effect-diagnostics nodeBuiltinImport:off processEnv:off
|
|
1188
1224
|
import { createRequire } from 'node:module';
|
|
@@ -1190,98 +1226,47 @@ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
|
1190
1226
|
import { dependencies } from './package.json';
|
|
1191
1227
|
|
|
1192
1228
|
const require = createRequire(import.meta.url);
|
|
1193
|
-
const runtimeVersion = (
|
|
1194
|
-
|
|
1195
|
-
).version;
|
|
1196
|
-
const reactVersion = (require('react/package.json') as { version: string })
|
|
1197
|
-
.version;
|
|
1198
|
-
const reactDomVersion = (
|
|
1199
|
-
require('react-dom/package.json') as { version: string }
|
|
1200
|
-
).version;
|
|
1229
|
+
const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
|
|
1230
|
+
const reactVersion = (require('react/package.json') as { version: string }).version;
|
|
1231
|
+
const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
|
|
1201
1232
|
|
|
1202
1233
|
export default createModuleFederationConfig({
|
|
1203
|
-
name: '${shellApp.mfName}',
|
|
1204
1234
|
dts: false,
|
|
1235
|
+
filename: 'remoteEntry.js',
|
|
1236
|
+
name: '${shellApp.mfName}',
|
|
1205
1237
|
remotes: {
|
|
1206
1238
|
commerce:
|
|
1207
1239
|
process.env['REMOTE_COMMERCE_MF_MANIFEST'] ??
|
|
1208
1240
|
'remoteCommerce@http://localhost:3021/mf-manifest.json',
|
|
1209
|
-
identity:
|
|
1210
|
-
process.env['REMOTE_IDENTITY_MF_MANIFEST'] ??
|
|
1211
|
-
'remoteIdentity@http://localhost:3022/mf-manifest.json',
|
|
1212
1241
|
designSystem:
|
|
1213
1242
|
process.env['REMOTE_DESIGN_SYSTEM_MF_MANIFEST'] ??
|
|
1214
1243
|
'remoteDesignSystem@http://localhost:3023/mf-manifest.json',
|
|
1244
|
+
identity:
|
|
1245
|
+
process.env['REMOTE_IDENTITY_MF_MANIFEST'] ??
|
|
1246
|
+
'remoteIdentity@http://localhost:3022/mf-manifest.json',
|
|
1215
1247
|
},
|
|
1216
|
-
|
|
1217
|
-
react: {
|
|
1218
|
-
singleton: true,
|
|
1219
|
-
requiredVersion: reactVersion,
|
|
1220
|
-
treeShaking: false,
|
|
1221
|
-
},
|
|
1222
|
-
'react-dom': {
|
|
1223
|
-
singleton: true,
|
|
1224
|
-
requiredVersion: reactDomVersion,
|
|
1225
|
-
treeShaking: false,
|
|
1226
|
-
},
|
|
1227
|
-
'@tanstack/react-router': {
|
|
1228
|
-
singleton: true,
|
|
1229
|
-
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1230
|
-
treeShaking: false,
|
|
1231
|
-
},
|
|
1232
|
-
'@modern-js/runtime': {
|
|
1233
|
-
singleton: true,
|
|
1234
|
-
requiredVersion: runtimeVersion,
|
|
1235
|
-
treeShaking: false,
|
|
1236
|
-
},
|
|
1237
|
-
},
|
|
1248
|
+
${createSharedModuleFederationConfig()},
|
|
1238
1249
|
});
|
|
1239
1250
|
`;
|
|
1240
1251
|
}
|
|
1241
1252
|
function createRemoteModuleFederationConfig(app) {
|
|
1242
|
-
const exposes =
|
|
1253
|
+
const exposes = formatTsObjectLiteral(app.exposes ?? {});
|
|
1243
1254
|
return `// @effect-diagnostics nodeBuiltinImport:off
|
|
1244
1255
|
import { createRequire } from 'node:module';
|
|
1245
1256
|
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
1246
1257
|
import { dependencies } from './package.json';
|
|
1247
1258
|
|
|
1248
1259
|
const require = createRequire(import.meta.url);
|
|
1249
|
-
const runtimeVersion = (
|
|
1250
|
-
|
|
1251
|
-
).version;
|
|
1252
|
-
const reactVersion = (require('react/package.json') as { version: string })
|
|
1253
|
-
.version;
|
|
1254
|
-
const reactDomVersion = (
|
|
1255
|
-
require('react-dom/package.json') as { version: string }
|
|
1256
|
-
).version;
|
|
1260
|
+
const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
|
|
1261
|
+
const reactVersion = (require('react/package.json') as { version: string }).version;
|
|
1262
|
+
const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
|
|
1257
1263
|
|
|
1258
1264
|
export default createModuleFederationConfig({
|
|
1259
|
-
name: '${app.mfName}',
|
|
1260
1265
|
dts: false,
|
|
1261
|
-
filename: 'remoteEntry.js',
|
|
1262
1266
|
exposes: ${exposes},
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
requiredVersion: reactVersion,
|
|
1267
|
-
treeShaking: false,
|
|
1268
|
-
},
|
|
1269
|
-
'react-dom': {
|
|
1270
|
-
singleton: true,
|
|
1271
|
-
requiredVersion: reactDomVersion,
|
|
1272
|
-
treeShaking: false,
|
|
1273
|
-
},
|
|
1274
|
-
'@tanstack/react-router': {
|
|
1275
|
-
singleton: true,
|
|
1276
|
-
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1277
|
-
treeShaking: false,
|
|
1278
|
-
},
|
|
1279
|
-
'@modern-js/runtime': {
|
|
1280
|
-
singleton: true,
|
|
1281
|
-
requiredVersion: runtimeVersion,
|
|
1282
|
-
treeShaking: false,
|
|
1283
|
-
},
|
|
1284
|
-
},
|
|
1267
|
+
filename: 'remoteEntry.js',
|
|
1268
|
+
name: '${app.mfName}',
|
|
1269
|
+
${createSharedModuleFederationConfig()},
|
|
1285
1270
|
});
|
|
1286
1271
|
`;
|
|
1287
1272
|
}
|
|
@@ -1296,19 +1281,19 @@ const port = Number(process.env['${effectService.portEnv}'] ?? ${effectService.p
|
|
|
1296
1281
|
export default defineConfig(
|
|
1297
1282
|
presetUltramodern(
|
|
1298
1283
|
{
|
|
1299
|
-
server: {
|
|
1300
|
-
port,
|
|
1301
|
-
},
|
|
1302
1284
|
bff: {
|
|
1303
|
-
prefix: '/recommendations-api',
|
|
1304
|
-
runtimeFramework: 'effect',
|
|
1305
1285
|
effect: {
|
|
1306
1286
|
openapi: {
|
|
1307
1287
|
path: '/openapi.json',
|
|
1308
1288
|
},
|
|
1309
1289
|
},
|
|
1290
|
+
prefix: '/recommendations-api',
|
|
1291
|
+
runtimeFramework: 'effect',
|
|
1310
1292
|
},
|
|
1311
1293
|
plugins: [appTools(), bffPlugin()],
|
|
1294
|
+
server: {
|
|
1295
|
+
port,
|
|
1296
|
+
},
|
|
1312
1297
|
},
|
|
1313
1298
|
{
|
|
1314
1299
|
appId,
|
|
@@ -1320,21 +1305,64 @@ export default defineConfig(
|
|
|
1320
1305
|
);
|
|
1321
1306
|
`;
|
|
1322
1307
|
}
|
|
1308
|
+
function createAppRuntimeConfig() {
|
|
1309
|
+
return `import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
1310
|
+
import { createInstance } from 'i18next';
|
|
1311
|
+
|
|
1312
|
+
const i18nInstance = createInstance();
|
|
1313
|
+
|
|
1314
|
+
export default defineRuntimeConfig({
|
|
1315
|
+
i18n: {
|
|
1316
|
+
i18nInstance,
|
|
1317
|
+
initOptions: {
|
|
1318
|
+
defaultNS: 'translation',
|
|
1319
|
+
fallbackLng: 'en',
|
|
1320
|
+
interpolation: {
|
|
1321
|
+
escapeValue: false,
|
|
1322
|
+
},
|
|
1323
|
+
ns: ['translation'],
|
|
1324
|
+
supportedLngs: ['en', 'cs'],
|
|
1325
|
+
},
|
|
1326
|
+
},
|
|
1327
|
+
router: {
|
|
1328
|
+
framework: 'tanstack',
|
|
1329
|
+
},
|
|
1330
|
+
});
|
|
1331
|
+
`;
|
|
1332
|
+
}
|
|
1323
1333
|
function createShellPage() {
|
|
1324
|
-
return `
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
];
|
|
1334
|
+
return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
1335
|
+
import { useTranslation } from 'react-i18next';
|
|
1336
|
+
|
|
1337
|
+
const remotes = ['remote-commerce', 'remote-identity', 'remote-design-system'];
|
|
1329
1338
|
|
|
1330
1339
|
export default function ShellHome() {
|
|
1340
|
+
const { t } = useTranslation();
|
|
1341
|
+
const { changeLanguage, language } = useModernI18n();
|
|
1342
|
+
const languageOptions = [
|
|
1343
|
+
{ code: 'en', label: t('language.en') },
|
|
1344
|
+
{ code: 'cs', label: t('language.cs') },
|
|
1345
|
+
];
|
|
1346
|
+
|
|
1331
1347
|
return (
|
|
1332
1348
|
<main>
|
|
1333
|
-
<
|
|
1334
|
-
|
|
1349
|
+
<nav aria-label={t('language.switcher')}>
|
|
1350
|
+
{languageOptions.map((option) => (
|
|
1351
|
+
<button
|
|
1352
|
+
disabled={language === option.code}
|
|
1353
|
+
key={option.code}
|
|
1354
|
+
onClick={() => void changeLanguage(option.code)}
|
|
1355
|
+
type="button"
|
|
1356
|
+
>
|
|
1357
|
+
{option.label}
|
|
1358
|
+
</button>
|
|
1359
|
+
))}
|
|
1360
|
+
</nav>
|
|
1361
|
+
<h1>{t('shell.title')}</h1>
|
|
1362
|
+
<p data-testid="ultramodern-preset">{t('shell.preset')}</p>
|
|
1335
1363
|
<ul>
|
|
1336
|
-
{remotes.map(remote => (
|
|
1337
|
-
<li key={remote}>{remote}</li>
|
|
1364
|
+
{remotes.map((remote) => (
|
|
1365
|
+
<li key={remote}>{t(\`shell.remotes.\${remote}\`)}</li>
|
|
1338
1366
|
))}
|
|
1339
1367
|
</ul>
|
|
1340
1368
|
</main>
|
|
@@ -1343,11 +1371,15 @@ export default function ShellHome() {
|
|
|
1343
1371
|
`;
|
|
1344
1372
|
}
|
|
1345
1373
|
function createRemotePage(app) {
|
|
1346
|
-
return `
|
|
1374
|
+
return `import { useTranslation } from 'react-i18next';
|
|
1375
|
+
|
|
1376
|
+
export default function ${toPascalCase(app.id)}Home() {
|
|
1377
|
+
const { t } = useTranslation();
|
|
1378
|
+
|
|
1347
1379
|
return (
|
|
1348
1380
|
<main>
|
|
1349
|
-
<h1
|
|
1350
|
-
<p data-mf-role="${app.kind}"
|
|
1381
|
+
<h1>{t('remote.title')}</h1>
|
|
1382
|
+
<p data-mf-role="${app.kind}">{t('remote.domain')}</p>
|
|
1351
1383
|
</main>
|
|
1352
1384
|
);
|
|
1353
1385
|
}
|
|
@@ -1362,17 +1394,21 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|
|
1362
1394
|
`;
|
|
1363
1395
|
}
|
|
1364
1396
|
function createRemoteEntry(app) {
|
|
1365
|
-
const
|
|
1366
|
-
return `export { default } from './components/${
|
|
1397
|
+
const componentFile = 'remote-identity' === app.id ? 'identity-widget' : 'commerce-widget';
|
|
1398
|
+
return `export { default } from './components/${componentFile}';
|
|
1367
1399
|
`;
|
|
1368
1400
|
}
|
|
1369
1401
|
function createRemoteWidget(app) {
|
|
1370
1402
|
const componentName = 'remote-identity' === app.id ? 'IdentityWidget' : 'CommerceWidget';
|
|
1371
|
-
return `
|
|
1403
|
+
return `import { useTranslation } from 'react-i18next';
|
|
1404
|
+
|
|
1405
|
+
export default function ${componentName}() {
|
|
1406
|
+
const { t } = useTranslation();
|
|
1407
|
+
|
|
1372
1408
|
return (
|
|
1373
1409
|
<section data-mf-remote="${app.id}">
|
|
1374
|
-
<h2
|
|
1375
|
-
<p>
|
|
1410
|
+
<h2>{t('remote.widget.title')}</h2>
|
|
1411
|
+
<p>{t('remote.widget.body')}</p>
|
|
1376
1412
|
</section>
|
|
1377
1413
|
);
|
|
1378
1414
|
}
|
|
@@ -1381,7 +1417,7 @@ function createRemoteWidget(app) {
|
|
|
1381
1417
|
function createDesignButton() {
|
|
1382
1418
|
return `import { designTokens } from '../tokens';
|
|
1383
1419
|
|
|
1384
|
-
export default function Button({ label
|
|
1420
|
+
export default function Button({ label }: { label: string }) {
|
|
1385
1421
|
return (
|
|
1386
1422
|
<button
|
|
1387
1423
|
type="button"
|
|
@@ -1399,8 +1435,8 @@ export default function Button({ label = 'Design System Button' }: { label?: str
|
|
|
1399
1435
|
function createDesignTokens() {
|
|
1400
1436
|
return `export const designTokens = {
|
|
1401
1437
|
color: {
|
|
1402
|
-
foreground: '#133225',
|
|
1403
1438
|
accent: '#2f8f68',
|
|
1439
|
+
foreground: '#133225',
|
|
1404
1440
|
},
|
|
1405
1441
|
radius: {
|
|
1406
1442
|
control: '999px',
|
|
@@ -1408,6 +1444,62 @@ function createDesignTokens() {
|
|
|
1408
1444
|
} as const;
|
|
1409
1445
|
`;
|
|
1410
1446
|
}
|
|
1447
|
+
function createEnglishTranslations(app) {
|
|
1448
|
+
if ('shell' === app.kind) return {
|
|
1449
|
+
language: {
|
|
1450
|
+
cs: 'Czech',
|
|
1451
|
+
en: 'English',
|
|
1452
|
+
switcher: 'Language'
|
|
1453
|
+
},
|
|
1454
|
+
shell: {
|
|
1455
|
+
preset: 'presetUltramodern workspace',
|
|
1456
|
+
remotes: {
|
|
1457
|
+
'remote-commerce': 'Commerce Remote',
|
|
1458
|
+
'remote-design-system': 'Design System Remote',
|
|
1459
|
+
'remote-identity': 'Identity Remote'
|
|
1460
|
+
},
|
|
1461
|
+
title: 'UltraModern SuperApp Shell'
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
return {
|
|
1465
|
+
remote: {
|
|
1466
|
+
domain: app.domain ?? app.kind,
|
|
1467
|
+
title: app.displayName,
|
|
1468
|
+
widget: {
|
|
1469
|
+
body: 'vertical' === app.kind ? `Owns the ${app.domain} vertical route surface.` : 'Provides shared UI primitives for the workspace.',
|
|
1470
|
+
title: app.displayName
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function createCzechTranslations(app) {
|
|
1476
|
+
if ('shell' === app.kind) return {
|
|
1477
|
+
language: {
|
|
1478
|
+
cs: 'Cestina',
|
|
1479
|
+
en: 'Anglictina',
|
|
1480
|
+
switcher: 'Jazyk'
|
|
1481
|
+
},
|
|
1482
|
+
shell: {
|
|
1483
|
+
preset: 'presetUltramodern workspace',
|
|
1484
|
+
remotes: {
|
|
1485
|
+
'remote-commerce': 'Commerce remote',
|
|
1486
|
+
'remote-design-system': 'Design system remote',
|
|
1487
|
+
'remote-identity': 'Identity remote'
|
|
1488
|
+
},
|
|
1489
|
+
title: 'UltraModern SuperApp shell'
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
return {
|
|
1493
|
+
remote: {
|
|
1494
|
+
domain: app.domain ?? app.kind,
|
|
1495
|
+
title: app.displayName,
|
|
1496
|
+
widget: {
|
|
1497
|
+
body: 'vertical' === app.kind ? `Vlastni ${app.domain} vertical route surface.` : 'Poskytuje sdilene UI prvky pro workspace.',
|
|
1498
|
+
title: app.displayName
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1411
1503
|
function createEffectSharedApi() {
|
|
1412
1504
|
return `import {
|
|
1413
1505
|
HttpApi,
|
|
@@ -1421,9 +1513,7 @@ const recommendationSchema = Schema.Struct({
|
|
|
1421
1513
|
title: Schema.String,
|
|
1422
1514
|
});
|
|
1423
1515
|
|
|
1424
|
-
export const recommendationsEffectApi = HttpApi.make(
|
|
1425
|
-
'RecommendationsEffectApi',
|
|
1426
|
-
).add(
|
|
1516
|
+
export const recommendationsEffectApi = HttpApi.make('RecommendationsEffectApi').add(
|
|
1427
1517
|
HttpApiGroup.make('recommendations').add(
|
|
1428
1518
|
HttpApiEndpoint.get('list', '/effect/recommendations', {
|
|
1429
1519
|
success: Schema.Struct({
|
|
@@ -1446,7 +1536,7 @@ import { recommendationsEffectApi } from '../../shared/effect/api';
|
|
|
1446
1536
|
const recommendationsLayer = HttpApiBuilder.group(
|
|
1447
1537
|
recommendationsEffectApi,
|
|
1448
1538
|
'recommendations',
|
|
1449
|
-
handlers =>
|
|
1539
|
+
(handlers) =>
|
|
1450
1540
|
handlers.handle('list', () =>
|
|
1451
1541
|
Effect.succeed({
|
|
1452
1542
|
items: [
|
|
@@ -1749,16 +1839,19 @@ function writeApp(targetDir, scope, app, packageSource) {
|
|
|
1749
1839
|
writeJson(targetDir, `${app.directory}/tsconfig.json`, createPackageTsConfig(app.directory));
|
|
1750
1840
|
writeFile(targetDir, `${app.directory}/src/modern-app-env.d.ts`, "/// <reference types='@modern-js/app-tools/types' />\n");
|
|
1751
1841
|
writeFile(targetDir, `${app.directory}/modern.config.ts`, createAppModernConfig(app));
|
|
1842
|
+
writeFile(targetDir, `${app.directory}/src/modern.runtime.ts`, createAppRuntimeConfig());
|
|
1843
|
+
writeJson(targetDir, `${app.directory}/config/public/locales/en/translation.json`, createEnglishTranslations(app));
|
|
1844
|
+
writeJson(targetDir, `${app.directory}/config/public/locales/cs/translation.json`, createCzechTranslations(app));
|
|
1752
1845
|
writeFile(targetDir, `${app.directory}/module-federation.config.ts`, 'shell' === app.kind ? createShellModuleFederationConfig() : createRemoteModuleFederationConfig(app));
|
|
1753
1846
|
writeFile(targetDir, `${app.directory}/src/routes/layout.tsx`, createLayout(app.id));
|
|
1754
1847
|
writeFile(targetDir, `${app.directory}/src/routes/page.tsx`, 'shell' === app.kind ? createShellPage() : createRemotePage(app));
|
|
1755
1848
|
if ('vertical' === app.kind) {
|
|
1756
1849
|
writeFile(targetDir, `${app.directory}/src/remote-entry.tsx`, createRemoteEntry(app));
|
|
1757
|
-
const widgetFile = 'remote-identity' === app.id ? '
|
|
1850
|
+
const widgetFile = 'remote-identity' === app.id ? 'identity-widget.tsx' : 'commerce-widget.tsx';
|
|
1758
1851
|
writeFile(targetDir, `${app.directory}/src/components/${widgetFile}`, createRemoteWidget(app));
|
|
1759
1852
|
}
|
|
1760
1853
|
if ('horizontal-design-system' === app.kind) {
|
|
1761
|
-
writeFile(targetDir, `${app.directory}/src/components/
|
|
1854
|
+
writeFile(targetDir, `${app.directory}/src/components/button.tsx`, createDesignButton());
|
|
1762
1855
|
writeFile(targetDir, `${app.directory}/src/tokens.ts`, createDesignTokens());
|
|
1763
1856
|
}
|
|
1764
1857
|
}
|
|
@@ -1785,27 +1878,27 @@ function writeSharedPackages(targetDir, scope) {
|
|
|
1785
1878
|
});
|
|
1786
1879
|
}
|
|
1787
1880
|
writeFile(targetDir, 'packages/shared-contracts/src/index.ts', `export const ultramodernWorkspaceContract = {
|
|
1881
|
+
ownership: 'topology/ownership.json',
|
|
1788
1882
|
preset: 'presetUltramodern',
|
|
1789
1883
|
topology: 'topology/reference-topology.json',
|
|
1790
|
-
ownership: 'topology/ownership.json',
|
|
1791
1884
|
} as const;
|
|
1792
1885
|
`);
|
|
1793
1886
|
writeFile(targetDir, 'packages/shared-design-tokens/src/index.ts', `export const sharedDesignTokens = {
|
|
1794
1887
|
color: {
|
|
1795
|
-
surface: '#f6fbf7',
|
|
1796
|
-
foreground: '#133225',
|
|
1797
1888
|
accent: '#2f8f68',
|
|
1889
|
+
foreground: '#133225',
|
|
1890
|
+
surface: '#f6fbf7',
|
|
1798
1891
|
},
|
|
1799
1892
|
} as const;
|
|
1800
1893
|
`);
|
|
1801
|
-
writeFile(targetDir, 'packages/shared-effect-api/src/index.ts', `export
|
|
1894
|
+
writeFile(targetDir, 'packages/shared-effect-api/src/index.ts', `export interface Recommendation {
|
|
1802
1895
|
id: string;
|
|
1803
1896
|
title: string;
|
|
1804
|
-
}
|
|
1897
|
+
}
|
|
1805
1898
|
|
|
1806
1899
|
export const recommendationsApiContract = {
|
|
1807
|
-
serviceId: '${effectService.id}',
|
|
1808
1900
|
basePath: '/recommendations-api/effect/recommendations',
|
|
1901
|
+
serviceId: '${effectService.id}',
|
|
1809
1902
|
} as const;
|
|
1810
1903
|
`);
|
|
1811
1904
|
}
|
|
@@ -1820,7 +1913,7 @@ function generateUltramodernWorkspace(options) {
|
|
|
1820
1913
|
packageScope: scope
|
|
1821
1914
|
});
|
|
1822
1915
|
writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource));
|
|
1823
|
-
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase(
|
|
1916
|
+
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase());
|
|
1824
1917
|
writeJson(options.targetDir, 'topology/reference-topology.json', createTopology(scope));
|
|
1825
1918
|
writeJson(options.targetDir, 'topology/ownership.json', createOwnership(scope));
|
|
1826
1919
|
writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay());
|
|
@@ -1904,7 +1997,7 @@ function detectBffRuntime() {
|
|
|
1904
1997
|
process.exit(1);
|
|
1905
1998
|
}
|
|
1906
1999
|
function src_renderTemplate(template, data) {
|
|
1907
|
-
const tagRegex = /\{\{(#if|#unless|\/if|\/unless)(?:\s+(\w+))
|
|
2000
|
+
const tagRegex = /\{\{(~?)(#if|#unless|\/if|\/unless)(?:\s+(\w+))?(~?)\}\}/g;
|
|
1908
2001
|
function renderConditionals(startIndex, expectedClose) {
|
|
1909
2002
|
let rendered = '';
|
|
1910
2003
|
let cursor = startIndex;
|
|
@@ -1915,7 +2008,7 @@ function src_renderTemplate(template, data) {
|
|
|
1915
2008
|
rendered: rendered + template.slice(cursor),
|
|
1916
2009
|
nextIndex: template.length
|
|
1917
2010
|
};
|
|
1918
|
-
const [raw, tag, condition] = match;
|
|
2011
|
+
const [raw, , tag, condition, rightTrim] = match;
|
|
1919
2012
|
const tagIndex = match.index;
|
|
1920
2013
|
rendered += template.slice(cursor, tagIndex);
|
|
1921
2014
|
cursor = tagIndex + raw.length;
|
|
@@ -1931,10 +2024,17 @@ function src_renderTemplate(template, data) {
|
|
|
1931
2024
|
}
|
|
1932
2025
|
if ('/if' === tag || '/unless' === tag) {
|
|
1933
2026
|
const kind = '/if' === tag ? 'if' : 'unless';
|
|
1934
|
-
if (expectedClose === kind)
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
2027
|
+
if (expectedClose === kind) {
|
|
2028
|
+
let nextIndex = cursor;
|
|
2029
|
+
if ('~' === rightTrim) {
|
|
2030
|
+
const trailingWhitespace = /^\s*/u.exec(template.slice(nextIndex));
|
|
2031
|
+
nextIndex += trailingWhitespace?.[0].length ?? 0;
|
|
2032
|
+
}
|
|
2033
|
+
return {
|
|
2034
|
+
rendered,
|
|
2035
|
+
nextIndex
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
1938
2038
|
rendered += raw;
|
|
1939
2039
|
}
|
|
1940
2040
|
}
|
|
@@ -2023,6 +2123,7 @@ function createBuiltinTemplateManifest(version) {
|
|
|
2023
2123
|
'AGENTS.md',
|
|
2024
2124
|
'README.md',
|
|
2025
2125
|
'api/**',
|
|
2126
|
+
'config/**',
|
|
2026
2127
|
'modern.config.ts',
|
|
2027
2128
|
'oxfmt.config.ts',
|
|
2028
2129
|
'oxlint.config.ts',
|
|
@@ -2417,6 +2518,7 @@ async function main() {
|
|
|
2417
2518
|
tsconfigVersion: singleAppModernPackageSpecifier('@modern-js/tsconfig', packageSource, useWorkspaceProtocol),
|
|
2418
2519
|
pluginTanstackVersion: singleAppModernPackageSpecifier('@modern-js/plugin-tanstack', packageSource, useWorkspaceProtocol),
|
|
2419
2520
|
pluginBffVersion: singleAppModernPackageSpecifier('@modern-js/plugin-bff', packageSource, useWorkspaceProtocol),
|
|
2521
|
+
pluginI18nVersion: singleAppModernPackageSpecifier('@modern-js/plugin-i18n', packageSource, useWorkspaceProtocol),
|
|
2420
2522
|
isSubproject,
|
|
2421
2523
|
routerFramework,
|
|
2422
2524
|
bffRuntime,
|
|
@@ -2496,6 +2598,7 @@ function copyTemplate(src, dest, options) {
|
|
|
2496
2598
|
tsconfigVersion: options.tsconfigVersion,
|
|
2497
2599
|
pluginTanstackVersion: options.pluginTanstackVersion,
|
|
2498
2600
|
pluginBffVersion: options.pluginBffVersion,
|
|
2601
|
+
pluginI18nVersion: options.pluginI18nVersion,
|
|
2499
2602
|
isSubproject: options.isSubproject,
|
|
2500
2603
|
isTanstackRouter: 'tanstack' === options.routerFramework,
|
|
2501
2604
|
enableBff: 'none' !== options.bffRuntime,
|