@bleedingdev/modern-js-create 3.2.0-ultramodern.10 → 3.2.0-ultramodern.11
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 +81 -130
- package/package.json +3 -3
- 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/validate-ultramodern-workspace.mjs.handlebars +93 -91
package/dist/index.js
CHANGED
|
@@ -631,7 +631,7 @@ const remoteApps = [
|
|
|
631
631
|
mfName: 'remoteCommerce',
|
|
632
632
|
exposes: {
|
|
633
633
|
'./Route': './src/remote-entry.tsx',
|
|
634
|
-
'./Widget': './src/components/
|
|
634
|
+
'./Widget': './src/components/commerce-widget.tsx'
|
|
635
635
|
},
|
|
636
636
|
ownership: {
|
|
637
637
|
team: 'commerce-experience',
|
|
@@ -660,7 +660,7 @@ const remoteApps = [
|
|
|
660
660
|
mfName: 'remoteIdentity',
|
|
661
661
|
exposes: {
|
|
662
662
|
'./Route': './src/remote-entry.tsx',
|
|
663
|
-
'./Widget': './src/components/
|
|
663
|
+
'./Widget': './src/components/identity-widget.tsx'
|
|
664
664
|
},
|
|
665
665
|
ownership: {
|
|
666
666
|
team: 'identity-platform',
|
|
@@ -688,7 +688,7 @@ const remoteApps = [
|
|
|
688
688
|
port: 3023,
|
|
689
689
|
mfName: 'remoteDesignSystem',
|
|
690
690
|
exposes: {
|
|
691
|
-
'./Button': './src/components/
|
|
691
|
+
'./Button': './src/components/button.tsx',
|
|
692
692
|
'./tokens': './src/tokens.ts'
|
|
693
693
|
},
|
|
694
694
|
ownership: {
|
|
@@ -943,6 +943,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
943
943
|
private: true,
|
|
944
944
|
name: scope,
|
|
945
945
|
version: '0.1.0',
|
|
946
|
+
type: 'module',
|
|
946
947
|
packageManager: 'pnpm@11.1.2',
|
|
947
948
|
scripts: {
|
|
948
949
|
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`,
|
|
@@ -956,7 +957,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
956
957
|
'format:check': 'oxfmt --check .',
|
|
957
958
|
lint: 'oxlint .',
|
|
958
959
|
'lint:fix': 'oxlint . --fix',
|
|
959
|
-
typecheck:
|
|
960
|
+
typecheck: `pnpm -r --filter "@${scope}/*" typecheck`,
|
|
960
961
|
'skills:install': "node ./scripts/bootstrap-agent-skills.mjs",
|
|
961
962
|
'skills:check': "node ./scripts/bootstrap-agent-skills.mjs --check",
|
|
962
963
|
'ultramodern:check': "node ./scripts/validate-ultramodern-workspace.mjs",
|
|
@@ -991,7 +992,7 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
991
992
|
}
|
|
992
993
|
};
|
|
993
994
|
}
|
|
994
|
-
function createTsConfigBase(
|
|
995
|
+
function createTsConfigBase() {
|
|
995
996
|
return {
|
|
996
997
|
compilerOptions: {
|
|
997
998
|
target: 'ESNext',
|
|
@@ -1018,12 +1019,6 @@ function createTsConfigBase(scope) {
|
|
|
1018
1019
|
noImplicitReturns: true,
|
|
1019
1020
|
skipLibCheck: true,
|
|
1020
1021
|
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
1022
|
plugins: [
|
|
1028
1023
|
{
|
|
1029
1024
|
name: '@effect/language-service',
|
|
@@ -1051,20 +1046,6 @@ function createPackageTsConfig(packageDir, includeApi = false) {
|
|
|
1051
1046
|
if (includeApi) include.push('api', 'shared');
|
|
1052
1047
|
return {
|
|
1053
1048
|
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
1049
|
include
|
|
1069
1050
|
};
|
|
1070
1051
|
}
|
|
@@ -1154,6 +1135,12 @@ const port = Number(process.env['${app.portEnv}'] ?? ${app.port});
|
|
|
1154
1135
|
export default defineConfig(
|
|
1155
1136
|
presetUltramodern(
|
|
1156
1137
|
{
|
|
1138
|
+
output: {
|
|
1139
|
+
disableTsChecker: true,
|
|
1140
|
+
polyfill: 'off',
|
|
1141
|
+
splitRouteChunks: false,
|
|
1142
|
+
},
|
|
1143
|
+
plugins: [appTools(), tanstackRouterPlugin(), moduleFederationPlugin()],
|
|
1157
1144
|
server: {
|
|
1158
1145
|
port,
|
|
1159
1146
|
ssr: {
|
|
@@ -1161,21 +1148,11 @@ export default defineConfig(
|
|
|
1161
1148
|
moduleFederationAppSSR: true,
|
|
1162
1149
|
},
|
|
1163
1150
|
},
|
|
1164
|
-
output: {
|
|
1165
|
-
polyfill: 'off',
|
|
1166
|
-
disableTsChecker: true,
|
|
1167
|
-
splitRouteChunks: false,
|
|
1168
|
-
},
|
|
1169
|
-
plugins: [
|
|
1170
|
-
appTools(),
|
|
1171
|
-
tanstackRouterPlugin(),
|
|
1172
|
-
moduleFederationPlugin(),
|
|
1173
|
-
],
|
|
1174
1151
|
},
|
|
1175
1152
|
{
|
|
1176
1153
|
appId,
|
|
1177
|
-
enableModuleFederationSSR: true,
|
|
1178
1154
|
enableBffRequestId: true,
|
|
1155
|
+
enableModuleFederationSSR: true,
|
|
1179
1156
|
enableTelemetryExporters: true,
|
|
1180
1157
|
telemetryFailLoudStartup: false,
|
|
1181
1158
|
},
|
|
@@ -1183,6 +1160,37 @@ export default defineConfig(
|
|
|
1183
1160
|
);
|
|
1184
1161
|
`;
|
|
1185
1162
|
}
|
|
1163
|
+
function createSharedModuleFederationConfig() {
|
|
1164
|
+
return ` shared: {
|
|
1165
|
+
'@modern-js/runtime': {
|
|
1166
|
+
requiredVersion: runtimeVersion,
|
|
1167
|
+
singleton: true,
|
|
1168
|
+
treeShaking: false,
|
|
1169
|
+
},
|
|
1170
|
+
'@tanstack/react-router': {
|
|
1171
|
+
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1172
|
+
singleton: true,
|
|
1173
|
+
treeShaking: false,
|
|
1174
|
+
},
|
|
1175
|
+
react: {
|
|
1176
|
+
requiredVersion: reactVersion,
|
|
1177
|
+
singleton: true,
|
|
1178
|
+
treeShaking: false,
|
|
1179
|
+
},
|
|
1180
|
+
'react-dom': {
|
|
1181
|
+
requiredVersion: reactDomVersion,
|
|
1182
|
+
singleton: true,
|
|
1183
|
+
treeShaking: false,
|
|
1184
|
+
},
|
|
1185
|
+
}`;
|
|
1186
|
+
}
|
|
1187
|
+
function formatTsObjectLiteral(value) {
|
|
1188
|
+
const entries = Object.entries(value).sort(([left], [right])=>left.localeCompare(right));
|
|
1189
|
+
if (0 === entries.length) return '{}';
|
|
1190
|
+
return `{
|
|
1191
|
+
${entries.map(([key, entryValue])=>` '${key}': '${entryValue}',`).join('\n')}
|
|
1192
|
+
}`;
|
|
1193
|
+
}
|
|
1186
1194
|
function createShellModuleFederationConfig() {
|
|
1187
1195
|
return `// @effect-diagnostics nodeBuiltinImport:off processEnv:off
|
|
1188
1196
|
import { createRequire } from 'node:module';
|
|
@@ -1190,98 +1198,47 @@ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
|
1190
1198
|
import { dependencies } from './package.json';
|
|
1191
1199
|
|
|
1192
1200
|
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;
|
|
1201
|
+
const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
|
|
1202
|
+
const reactVersion = (require('react/package.json') as { version: string }).version;
|
|
1203
|
+
const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
|
|
1201
1204
|
|
|
1202
1205
|
export default createModuleFederationConfig({
|
|
1203
|
-
name: '${shellApp.mfName}',
|
|
1204
1206
|
dts: false,
|
|
1207
|
+
filename: 'remoteEntry.js',
|
|
1208
|
+
name: '${shellApp.mfName}',
|
|
1205
1209
|
remotes: {
|
|
1206
1210
|
commerce:
|
|
1207
1211
|
process.env['REMOTE_COMMERCE_MF_MANIFEST'] ??
|
|
1208
1212
|
'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
1213
|
designSystem:
|
|
1213
1214
|
process.env['REMOTE_DESIGN_SYSTEM_MF_MANIFEST'] ??
|
|
1214
1215
|
'remoteDesignSystem@http://localhost:3023/mf-manifest.json',
|
|
1216
|
+
identity:
|
|
1217
|
+
process.env['REMOTE_IDENTITY_MF_MANIFEST'] ??
|
|
1218
|
+
'remoteIdentity@http://localhost:3022/mf-manifest.json',
|
|
1215
1219
|
},
|
|
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
|
-
},
|
|
1220
|
+
${createSharedModuleFederationConfig()},
|
|
1238
1221
|
});
|
|
1239
1222
|
`;
|
|
1240
1223
|
}
|
|
1241
1224
|
function createRemoteModuleFederationConfig(app) {
|
|
1242
|
-
const exposes =
|
|
1225
|
+
const exposes = formatTsObjectLiteral(app.exposes ?? {});
|
|
1243
1226
|
return `// @effect-diagnostics nodeBuiltinImport:off
|
|
1244
1227
|
import { createRequire } from 'node:module';
|
|
1245
1228
|
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
1246
1229
|
import { dependencies } from './package.json';
|
|
1247
1230
|
|
|
1248
1231
|
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;
|
|
1232
|
+
const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
|
|
1233
|
+
const reactVersion = (require('react/package.json') as { version: string }).version;
|
|
1234
|
+
const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
|
|
1257
1235
|
|
|
1258
1236
|
export default createModuleFederationConfig({
|
|
1259
|
-
name: '${app.mfName}',
|
|
1260
1237
|
dts: false,
|
|
1261
|
-
filename: 'remoteEntry.js',
|
|
1262
1238
|
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
|
-
},
|
|
1239
|
+
filename: 'remoteEntry.js',
|
|
1240
|
+
name: '${app.mfName}',
|
|
1241
|
+
${createSharedModuleFederationConfig()},
|
|
1285
1242
|
});
|
|
1286
1243
|
`;
|
|
1287
1244
|
}
|
|
@@ -1296,19 +1253,19 @@ const port = Number(process.env['${effectService.portEnv}'] ?? ${effectService.p
|
|
|
1296
1253
|
export default defineConfig(
|
|
1297
1254
|
presetUltramodern(
|
|
1298
1255
|
{
|
|
1299
|
-
server: {
|
|
1300
|
-
port,
|
|
1301
|
-
},
|
|
1302
1256
|
bff: {
|
|
1303
|
-
prefix: '/recommendations-api',
|
|
1304
|
-
runtimeFramework: 'effect',
|
|
1305
1257
|
effect: {
|
|
1306
1258
|
openapi: {
|
|
1307
1259
|
path: '/openapi.json',
|
|
1308
1260
|
},
|
|
1309
1261
|
},
|
|
1262
|
+
prefix: '/recommendations-api',
|
|
1263
|
+
runtimeFramework: 'effect',
|
|
1310
1264
|
},
|
|
1311
1265
|
plugins: [appTools(), bffPlugin()],
|
|
1266
|
+
server: {
|
|
1267
|
+
port,
|
|
1268
|
+
},
|
|
1312
1269
|
},
|
|
1313
1270
|
{
|
|
1314
1271
|
appId,
|
|
@@ -1321,11 +1278,7 @@ export default defineConfig(
|
|
|
1321
1278
|
`;
|
|
1322
1279
|
}
|
|
1323
1280
|
function createShellPage() {
|
|
1324
|
-
return `const remotes = [
|
|
1325
|
-
'remote-commerce',
|
|
1326
|
-
'remote-identity',
|
|
1327
|
-
'remote-design-system',
|
|
1328
|
-
];
|
|
1281
|
+
return `const remotes = ['remote-commerce', 'remote-identity', 'remote-design-system'];
|
|
1329
1282
|
|
|
1330
1283
|
export default function ShellHome() {
|
|
1331
1284
|
return (
|
|
@@ -1333,7 +1286,7 @@ export default function ShellHome() {
|
|
|
1333
1286
|
<h1>UltraModern SuperApp Shell</h1>
|
|
1334
1287
|
<p data-testid="ultramodern-preset">presetUltramodern workspace</p>
|
|
1335
1288
|
<ul>
|
|
1336
|
-
{remotes.map(remote => (
|
|
1289
|
+
{remotes.map((remote) => (
|
|
1337
1290
|
<li key={remote}>{remote}</li>
|
|
1338
1291
|
))}
|
|
1339
1292
|
</ul>
|
|
@@ -1362,8 +1315,8 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|
|
1362
1315
|
`;
|
|
1363
1316
|
}
|
|
1364
1317
|
function createRemoteEntry(app) {
|
|
1365
|
-
const
|
|
1366
|
-
return `export { default } from './components/${
|
|
1318
|
+
const componentFile = 'remote-identity' === app.id ? 'identity-widget' : 'commerce-widget';
|
|
1319
|
+
return `export { default } from './components/${componentFile}';
|
|
1367
1320
|
`;
|
|
1368
1321
|
}
|
|
1369
1322
|
function createRemoteWidget(app) {
|
|
@@ -1399,8 +1352,8 @@ export default function Button({ label = 'Design System Button' }: { label?: str
|
|
|
1399
1352
|
function createDesignTokens() {
|
|
1400
1353
|
return `export const designTokens = {
|
|
1401
1354
|
color: {
|
|
1402
|
-
foreground: '#133225',
|
|
1403
1355
|
accent: '#2f8f68',
|
|
1356
|
+
foreground: '#133225',
|
|
1404
1357
|
},
|
|
1405
1358
|
radius: {
|
|
1406
1359
|
control: '999px',
|
|
@@ -1421,9 +1374,7 @@ const recommendationSchema = Schema.Struct({
|
|
|
1421
1374
|
title: Schema.String,
|
|
1422
1375
|
});
|
|
1423
1376
|
|
|
1424
|
-
export const recommendationsEffectApi = HttpApi.make(
|
|
1425
|
-
'RecommendationsEffectApi',
|
|
1426
|
-
).add(
|
|
1377
|
+
export const recommendationsEffectApi = HttpApi.make('RecommendationsEffectApi').add(
|
|
1427
1378
|
HttpApiGroup.make('recommendations').add(
|
|
1428
1379
|
HttpApiEndpoint.get('list', '/effect/recommendations', {
|
|
1429
1380
|
success: Schema.Struct({
|
|
@@ -1446,7 +1397,7 @@ import { recommendationsEffectApi } from '../../shared/effect/api';
|
|
|
1446
1397
|
const recommendationsLayer = HttpApiBuilder.group(
|
|
1447
1398
|
recommendationsEffectApi,
|
|
1448
1399
|
'recommendations',
|
|
1449
|
-
handlers =>
|
|
1400
|
+
(handlers) =>
|
|
1450
1401
|
handlers.handle('list', () =>
|
|
1451
1402
|
Effect.succeed({
|
|
1452
1403
|
items: [
|
|
@@ -1754,11 +1705,11 @@ function writeApp(targetDir, scope, app, packageSource) {
|
|
|
1754
1705
|
writeFile(targetDir, `${app.directory}/src/routes/page.tsx`, 'shell' === app.kind ? createShellPage() : createRemotePage(app));
|
|
1755
1706
|
if ('vertical' === app.kind) {
|
|
1756
1707
|
writeFile(targetDir, `${app.directory}/src/remote-entry.tsx`, createRemoteEntry(app));
|
|
1757
|
-
const widgetFile = 'remote-identity' === app.id ? '
|
|
1708
|
+
const widgetFile = 'remote-identity' === app.id ? 'identity-widget.tsx' : 'commerce-widget.tsx';
|
|
1758
1709
|
writeFile(targetDir, `${app.directory}/src/components/${widgetFile}`, createRemoteWidget(app));
|
|
1759
1710
|
}
|
|
1760
1711
|
if ('horizontal-design-system' === app.kind) {
|
|
1761
|
-
writeFile(targetDir, `${app.directory}/src/components/
|
|
1712
|
+
writeFile(targetDir, `${app.directory}/src/components/button.tsx`, createDesignButton());
|
|
1762
1713
|
writeFile(targetDir, `${app.directory}/src/tokens.ts`, createDesignTokens());
|
|
1763
1714
|
}
|
|
1764
1715
|
}
|
|
@@ -1785,27 +1736,27 @@ function writeSharedPackages(targetDir, scope) {
|
|
|
1785
1736
|
});
|
|
1786
1737
|
}
|
|
1787
1738
|
writeFile(targetDir, 'packages/shared-contracts/src/index.ts', `export const ultramodernWorkspaceContract = {
|
|
1739
|
+
ownership: 'topology/ownership.json',
|
|
1788
1740
|
preset: 'presetUltramodern',
|
|
1789
1741
|
topology: 'topology/reference-topology.json',
|
|
1790
|
-
ownership: 'topology/ownership.json',
|
|
1791
1742
|
} as const;
|
|
1792
1743
|
`);
|
|
1793
1744
|
writeFile(targetDir, 'packages/shared-design-tokens/src/index.ts', `export const sharedDesignTokens = {
|
|
1794
1745
|
color: {
|
|
1795
|
-
surface: '#f6fbf7',
|
|
1796
|
-
foreground: '#133225',
|
|
1797
1746
|
accent: '#2f8f68',
|
|
1747
|
+
foreground: '#133225',
|
|
1748
|
+
surface: '#f6fbf7',
|
|
1798
1749
|
},
|
|
1799
1750
|
} as const;
|
|
1800
1751
|
`);
|
|
1801
|
-
writeFile(targetDir, 'packages/shared-effect-api/src/index.ts', `export
|
|
1752
|
+
writeFile(targetDir, 'packages/shared-effect-api/src/index.ts', `export interface Recommendation {
|
|
1802
1753
|
id: string;
|
|
1803
1754
|
title: string;
|
|
1804
|
-
}
|
|
1755
|
+
}
|
|
1805
1756
|
|
|
1806
1757
|
export const recommendationsApiContract = {
|
|
1807
|
-
serviceId: '${effectService.id}',
|
|
1808
1758
|
basePath: '/recommendations-api/effect/recommendations',
|
|
1759
|
+
serviceId: '${effectService.id}',
|
|
1809
1760
|
} as const;
|
|
1810
1761
|
`);
|
|
1811
1762
|
}
|
|
@@ -1820,7 +1771,7 @@ function generateUltramodernWorkspace(options) {
|
|
|
1820
1771
|
packageScope: scope
|
|
1821
1772
|
});
|
|
1822
1773
|
writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource));
|
|
1823
|
-
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase(
|
|
1774
|
+
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase());
|
|
1824
1775
|
writeJson(options.targetDir, 'topology/reference-topology.json', createTopology(scope));
|
|
1825
1776
|
writeJson(options.targetDir, 'topology/ownership.json', createOwnership(scope));
|
|
1826
1777
|
writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay());
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
|
-
"version": "3.2.0-ultramodern.
|
|
24
|
+
"version": "3.2.0-ultramodern.11",
|
|
25
25
|
"types": "./dist/types/index.d.ts",
|
|
26
26
|
"main": "./dist/index.js",
|
|
27
27
|
"bin": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@types/node": "^25.8.0",
|
|
42
42
|
"@typescript/native-preview": "7.0.0-dev.20260516.1",
|
|
43
43
|
"tsx": "^4.22.0",
|
|
44
|
-
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.
|
|
44
|
+
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.11"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
"start": "node ./dist/index.js"
|
|
55
55
|
},
|
|
56
56
|
"ultramodern": {
|
|
57
|
-
"frameworkVersion": "3.2.0-ultramodern.
|
|
57
|
+
"frameworkVersion": "3.2.0-ultramodern.11"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
2
|
-
import ultracite from
|
|
1
|
+
import { defineConfig } from 'oxfmt';
|
|
2
|
+
import ultracite from 'ultracite/oxfmt';
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
extends: [ultracite],
|
|
6
|
-
ignorePatterns: [
|
|
6
|
+
ignorePatterns: [
|
|
7
|
+
'.agents',
|
|
8
|
+
'**/*.json',
|
|
9
|
+
'dist',
|
|
10
|
+
'node_modules',
|
|
11
|
+
'.modern',
|
|
12
|
+
'.modernjs',
|
|
13
|
+
'**/routeTree.gen.ts',
|
|
14
|
+
],
|
|
15
|
+
singleQuote: true,
|
|
7
16
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
2
|
-
import core from
|
|
3
|
-
import react from
|
|
1
|
+
import { defineConfig } from 'oxlint';
|
|
2
|
+
import core from 'ultracite/oxlint/core';
|
|
3
|
+
import react from 'ultracite/oxlint/react';
|
|
4
4
|
|
|
5
5
|
export default defineConfig({
|
|
6
6
|
env: {
|
|
@@ -8,5 +8,12 @@ export default defineConfig({
|
|
|
8
8
|
node: true,
|
|
9
9
|
},
|
|
10
10
|
extends: [core, react],
|
|
11
|
-
ignorePatterns: [
|
|
11
|
+
ignorePatterns: [
|
|
12
|
+
'.agents',
|
|
13
|
+
'dist',
|
|
14
|
+
'node_modules',
|
|
15
|
+
'.modern',
|
|
16
|
+
'.modernjs',
|
|
17
|
+
'**/routeTree.gen.ts',
|
|
18
|
+
],
|
|
12
19
|
});
|
|
@@ -8,43 +8,37 @@ const lockPath = path.join(root, '.agents/skills-lock.json');
|
|
|
8
8
|
const checkOnly = process.argv.includes('--check');
|
|
9
9
|
const force = process.argv.includes('--force');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
13
|
-
}
|
|
11
|
+
const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const run = (command, args, options = {}) =>
|
|
14
|
+
execFileSync(command, args, {
|
|
17
15
|
cwd: options.cwd ?? root,
|
|
18
|
-
encoding: '
|
|
16
|
+
encoding: 'utf-8',
|
|
19
17
|
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
20
18
|
});
|
|
21
|
-
}
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
const repo = source.repository.replace(/^https:\/\/github.com
|
|
20
|
+
const cloneSource = (source, targetDir) => {
|
|
21
|
+
const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
|
|
25
22
|
try {
|
|
26
23
|
run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
|
|
27
24
|
stdio: 'inherit',
|
|
28
25
|
});
|
|
29
|
-
return;
|
|
30
26
|
} catch {
|
|
31
27
|
run('git', ['clone', '--depth', '1', source.repository, targetDir], {
|
|
32
28
|
stdio: 'inherit',
|
|
33
29
|
});
|
|
34
30
|
}
|
|
35
|
-
}
|
|
31
|
+
};
|
|
36
32
|
|
|
37
|
-
|
|
33
|
+
const resolveSkillDir = (sourceRoot, skillName) => {
|
|
38
34
|
const candidates = [
|
|
39
35
|
path.join(sourceRoot, skillName),
|
|
40
36
|
path.join(sourceRoot, 'skills', skillName),
|
|
41
37
|
path.join(sourceRoot, 'skills', 'engineering', skillName),
|
|
42
38
|
path.join(sourceRoot, 'skills', 'productivity', skillName),
|
|
43
39
|
];
|
|
44
|
-
return candidates.find(candidate =>
|
|
45
|
-
|
|
46
|
-
);
|
|
47
|
-
}
|
|
40
|
+
return candidates.find((candidate) => fs.existsSync(path.join(candidate, 'SKILL.md')));
|
|
41
|
+
};
|
|
48
42
|
|
|
49
43
|
if (!fs.existsSync(lockPath)) {
|
|
50
44
|
console.error('Missing .agents/skills-lock.json');
|
|
@@ -54,17 +48,14 @@ if (!fs.existsSync(lockPath)) {
|
|
|
54
48
|
const lock = readJson(lockPath);
|
|
55
49
|
const installDir = path.join(root, lock.installDir ?? '.agents/skills');
|
|
56
50
|
const privateSources = (lock.sources ?? []).filter(
|
|
57
|
-
source => source.install === 'clone-if-authorized',
|
|
51
|
+
(source) => source.install === 'clone-if-authorized',
|
|
58
52
|
);
|
|
59
53
|
|
|
60
54
|
if (checkOnly) {
|
|
61
|
-
const missing = privateSources.flatMap(source =>
|
|
55
|
+
const missing = privateSources.flatMap((source) =>
|
|
62
56
|
(source.baseline ?? [])
|
|
63
|
-
.map(skill => skill.name)
|
|
64
|
-
.filter(
|
|
65
|
-
skillName =>
|
|
66
|
-
!fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
67
|
-
),
|
|
57
|
+
.map((skill) => skill.name)
|
|
58
|
+
.filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md'))),
|
|
68
59
|
);
|
|
69
60
|
if (missing.length > 0) {
|
|
70
61
|
console.warn(
|
|
@@ -85,9 +76,7 @@ for (const source of privateSources) {
|
|
|
85
76
|
for (const skill of source.baseline ?? []) {
|
|
86
77
|
const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
|
|
87
78
|
if (!sourceSkillDir) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`Skill ${skill.name} not found in ${source.repository}`,
|
|
90
|
-
);
|
|
79
|
+
throw new Error(`Skill ${skill.name} not found in ${source.repository}`);
|
|
91
80
|
}
|
|
92
81
|
const targetSkillDir = path.join(installDir, skill.name);
|
|
93
82
|
if (fs.existsSync(targetSkillDir)) {
|
|
@@ -95,12 +84,12 @@ for (const source of privateSources) {
|
|
|
95
84
|
console.log(`Skipping existing ${skill.name}`);
|
|
96
85
|
continue;
|
|
97
86
|
}
|
|
98
|
-
fs.rmSync(targetSkillDir, {
|
|
87
|
+
fs.rmSync(targetSkillDir, { force: true, recursive: true });
|
|
99
88
|
}
|
|
100
89
|
fs.cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
|
|
101
90
|
console.log(`Installed ${skill.name}`);
|
|
102
91
|
}
|
|
103
92
|
} finally {
|
|
104
|
-
fs.rmSync(tempDir, {
|
|
93
|
+
fs.rmSync(tempDir, { force: true, recursive: true });
|
|
105
94
|
}
|
|
106
95
|
}
|
|
@@ -4,8 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
const root = process.cwd();
|
|
5
5
|
const packageScope = '{{packageScope}}';
|
|
6
6
|
const tanstackVersion = '1.170.1';
|
|
7
|
-
const rstackAgentSkillsCommit =
|
|
8
|
-
'61c948b42512e223bad44b83af4080eba48b2677';
|
|
7
|
+
const rstackAgentSkillsCommit = '61c948b42512e223bad44b83af4080eba48b2677';
|
|
9
8
|
const modernPackages = [
|
|
10
9
|
'@modern-js/app-tools',
|
|
11
10
|
'@modern-js/plugin-bff',
|
|
@@ -21,31 +20,18 @@ const baselineAgentSkills = [
|
|
|
21
20
|
'rslib-modern-package',
|
|
22
21
|
'rstest-best-practices',
|
|
23
22
|
];
|
|
24
|
-
const privateAgentSkills = [
|
|
25
|
-
'plan-graph',
|
|
26
|
-
'dag',
|
|
27
|
-
'subagent-graph',
|
|
28
|
-
'helm',
|
|
29
|
-
'debugger-mode',
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
function readText(relativePath) {
|
|
33
|
-
return fs.readFileSync(path.join(root, relativePath), 'utf-8');
|
|
34
|
-
}
|
|
23
|
+
const privateAgentSkills = ['plan-graph', 'dag', 'subagent-graph', 'helm', 'debugger-mode'];
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function assert(condition, message) {
|
|
25
|
+
const readText = (relativePath) => fs.readFileSync(path.join(root, relativePath), 'utf-8');
|
|
26
|
+
const readJson = (relativePath) => JSON.parse(readText(relativePath));
|
|
27
|
+
const assert = (condition, message) => {
|
|
41
28
|
if (!condition) {
|
|
42
29
|
throw new Error(message);
|
|
43
30
|
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function assertExists(relativePath) {
|
|
31
|
+
};
|
|
32
|
+
const assertExists = (relativePath) => {
|
|
47
33
|
assert(fs.existsSync(path.join(root, relativePath)), `Missing ${relativePath}`);
|
|
48
|
-
}
|
|
34
|
+
};
|
|
49
35
|
|
|
50
36
|
const requiredPaths = [
|
|
51
37
|
'AGENTS.md',
|
|
@@ -101,11 +87,9 @@ const rootPackage = readJson('package.json');
|
|
|
101
87
|
const packageSource = readJson('.modernjs/ultramodern-package-source.json');
|
|
102
88
|
const skillsLock = readJson('.agents/skills-lock.json');
|
|
103
89
|
const expectedModernSpecifier =
|
|
104
|
-
packageSource.strategy === 'install'
|
|
105
|
-
? packageSource.modernPackages?.specifier
|
|
106
|
-
: 'workspace:*';
|
|
90
|
+
packageSource.strategy === 'install' ? packageSource.modernPackages?.specifier : 'workspace:*';
|
|
107
91
|
|
|
108
|
-
|
|
92
|
+
const expectedModernDependency = (packageName) => {
|
|
109
93
|
if (packageSource.strategy !== 'install') {
|
|
110
94
|
return 'workspace:*';
|
|
111
95
|
}
|
|
@@ -114,15 +98,14 @@ function expectedModernDependency(packageName) {
|
|
|
114
98
|
return aliasPackageName
|
|
115
99
|
? `npm:${aliasPackageName}@${expectedModernSpecifier}`
|
|
116
100
|
: expectedModernSpecifier;
|
|
117
|
-
}
|
|
101
|
+
};
|
|
118
102
|
|
|
119
103
|
assert(rootPackage.private === true, 'Root package must be private');
|
|
120
104
|
assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
|
|
121
105
|
assert(rootPackage.packageManager === 'pnpm@11.1.2', 'Root must pin pnpm 11.1.2');
|
|
122
106
|
assert(rootPackage.engines?.pnpm === '>=11.0.0', 'Root must require pnpm >=11');
|
|
123
107
|
assert(
|
|
124
|
-
rootPackage.modernjs?.packageSource?.config ===
|
|
125
|
-
'./.modernjs/ultramodern-package-source.json',
|
|
108
|
+
rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-package-source.json',
|
|
126
109
|
'Root must point to the UltraModern package source metadata',
|
|
127
110
|
);
|
|
128
111
|
assert(
|
|
@@ -138,46 +121,38 @@ assert(
|
|
|
138
121
|
'Generated shared packages must keep workspace:* links',
|
|
139
122
|
);
|
|
140
123
|
assert(
|
|
141
|
-
modernPackages.every(packageName =>
|
|
124
|
+
modernPackages.every((packageName) =>
|
|
142
125
|
packageSource.modernPackages?.packages?.includes(packageName),
|
|
143
126
|
),
|
|
144
127
|
'Package source metadata must list all Modern runtime/tooling packages',
|
|
145
128
|
);
|
|
146
129
|
assert(
|
|
147
|
-
expectedModernSpecifier &&
|
|
148
|
-
packageSource.modernPackages?.specifier === expectedModernSpecifier,
|
|
130
|
+
expectedModernSpecifier && packageSource.modernPackages?.specifier === expectedModernSpecifier,
|
|
149
131
|
'Package source metadata must provide a Modern package specifier',
|
|
150
132
|
);
|
|
151
|
-
|
|
152
|
-
rootPackage.scripts?.['ultramodern:check'] ===
|
|
153
|
-
'node ./scripts/validate-ultramodern-workspace.mjs',
|
|
154
|
-
'Root must expose the ultramodern:check script',
|
|
155
|
-
);
|
|
133
|
+
|
|
156
134
|
const requiredRootScripts = {
|
|
157
135
|
format: 'oxfmt .',
|
|
158
136
|
'format:check': 'oxfmt --check .',
|
|
159
137
|
lint: 'oxlint .',
|
|
160
138
|
'lint:fix': 'oxlint . --fix',
|
|
161
|
-
'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
|
|
162
139
|
'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
|
|
140
|
+
'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
|
|
141
|
+
typecheck: `pnpm -r --filter "@${packageScope}/*" typecheck`,
|
|
142
|
+
'ultramodern:check': 'node ./scripts/validate-ultramodern-workspace.mjs',
|
|
163
143
|
};
|
|
164
144
|
for (const [scriptName, scriptCommand] of Object.entries(requiredRootScripts)) {
|
|
165
|
-
assert(
|
|
166
|
-
rootPackage.scripts?.[scriptName] === scriptCommand,
|
|
167
|
-
`Root must expose ${scriptName}`,
|
|
168
|
-
);
|
|
145
|
+
assert(rootPackage.scripts?.[scriptName] === scriptCommand, `Root must expose ${scriptName}`);
|
|
169
146
|
}
|
|
147
|
+
|
|
170
148
|
for (const dependency of [
|
|
171
149
|
'@effect/tsgo',
|
|
172
150
|
'@typescript/native-preview',
|
|
173
|
-
'oxlint',
|
|
174
151
|
'oxfmt',
|
|
152
|
+
'oxlint',
|
|
175
153
|
'ultracite',
|
|
176
154
|
]) {
|
|
177
|
-
assert(
|
|
178
|
-
rootPackage.devDependencies?.[dependency],
|
|
179
|
-
`Root must depend on ${dependency}`,
|
|
180
|
-
);
|
|
155
|
+
assert(rootPackage.devDependencies?.[dependency], `Root must depend on ${dependency}`);
|
|
181
156
|
}
|
|
182
157
|
|
|
183
158
|
const agentsInstructions = readText('AGENTS.md');
|
|
@@ -200,17 +175,17 @@ assert(
|
|
|
200
175
|
);
|
|
201
176
|
for (const skillName of baselineAgentSkills) {
|
|
202
177
|
assert(
|
|
203
|
-
skillsLock.baseline?.some(skill => skill.name === skillName),
|
|
178
|
+
skillsLock.baseline?.some((skill) => skill.name === skillName),
|
|
204
179
|
`Agent skills lock must include ${skillName}`,
|
|
205
180
|
);
|
|
206
|
-
const skillContent = readText(`.agents/skills/${skillName}/SKILL.md`);
|
|
207
181
|
assert(
|
|
208
|
-
|
|
182
|
+
readText(`.agents/skills/${skillName}/SKILL.md`).includes(`name: ${skillName}`),
|
|
209
183
|
`${skillName} must contain matching skill metadata`,
|
|
210
184
|
);
|
|
211
185
|
}
|
|
186
|
+
|
|
212
187
|
const privateSource = skillsLock.sources?.find(
|
|
213
|
-
source => source.repository === 'https://github.com/TechsioCZ/skills',
|
|
188
|
+
(source) => source.repository === 'https://github.com/TechsioCZ/skills',
|
|
214
189
|
);
|
|
215
190
|
assert(
|
|
216
191
|
privateSource?.install === 'clone-if-authorized',
|
|
@@ -218,7 +193,7 @@ assert(
|
|
|
218
193
|
);
|
|
219
194
|
for (const skillName of privateAgentSkills) {
|
|
220
195
|
assert(
|
|
221
|
-
privateSource.baseline?.some(skill => skill.name === skillName),
|
|
196
|
+
privateSource.baseline?.some((skill) => skill.name === skillName),
|
|
222
197
|
`Agent skills lock must allowlist private skill ${skillName}`,
|
|
223
198
|
);
|
|
224
199
|
}
|
|
@@ -235,26 +210,30 @@ for (const packagePath of appPackagePaths) {
|
|
|
235
210
|
assert(
|
|
236
211
|
packageJson.dependencies?.['@modern-js/plugin-tanstack'] ===
|
|
237
212
|
expectedModernDependency('@modern-js/plugin-tanstack'),
|
|
238
|
-
`${packagePath} must
|
|
213
|
+
`${packagePath} must use @modern-js/plugin-tanstack through ${expectedModernDependency(
|
|
214
|
+
'@modern-js/plugin-tanstack',
|
|
215
|
+
)}`,
|
|
239
216
|
);
|
|
240
217
|
assert(
|
|
241
218
|
packageJson.dependencies?.['@modern-js/runtime'] ===
|
|
242
219
|
expectedModernDependency('@modern-js/runtime'),
|
|
243
|
-
`${packagePath} must
|
|
220
|
+
`${packagePath} must use @modern-js/runtime through ${expectedModernDependency(
|
|
221
|
+
'@modern-js/runtime',
|
|
222
|
+
)}`,
|
|
244
223
|
);
|
|
245
224
|
assert(
|
|
246
225
|
packageJson.devDependencies?.['@modern-js/app-tools'] ===
|
|
247
226
|
expectedModernDependency('@modern-js/app-tools'),
|
|
248
|
-
`${packagePath} must
|
|
227
|
+
`${packagePath} must use @modern-js/app-tools through ${expectedModernDependency(
|
|
228
|
+
'@modern-js/app-tools',
|
|
229
|
+
)}`,
|
|
249
230
|
);
|
|
250
231
|
assert(
|
|
251
|
-
packageJson.dependencies?.[`@${packageScope}/shared-contracts`] ===
|
|
252
|
-
'workspace:*',
|
|
232
|
+
packageJson.dependencies?.[`@${packageScope}/shared-contracts`] === 'workspace:*',
|
|
253
233
|
`${packagePath} must link generated shared contracts through workspace:*`,
|
|
254
234
|
);
|
|
255
235
|
assert(
|
|
256
|
-
packageJson.dependencies?.[`@${packageScope}/shared-design-tokens`] ===
|
|
257
|
-
'workspace:*',
|
|
236
|
+
packageJson.dependencies?.[`@${packageScope}/shared-design-tokens`] === 'workspace:*',
|
|
258
237
|
`${packagePath} must link generated shared design tokens through workspace:*`,
|
|
259
238
|
);
|
|
260
239
|
assert(
|
|
@@ -280,56 +259,83 @@ for (const configPath of [
|
|
|
280
259
|
const config = readText(configPath);
|
|
281
260
|
assert(config.includes('presetUltramodern('), `${configPath} must use presetUltramodern`);
|
|
282
261
|
assert(config.includes('tanstackRouterPlugin()'), `${configPath} must enable plugin-tanstack`);
|
|
283
|
-
assert(
|
|
262
|
+
assert(
|
|
263
|
+
config.includes('moduleFederationPlugin()'),
|
|
264
|
+
`${configPath} must enable Module Federation`,
|
|
265
|
+
);
|
|
284
266
|
}
|
|
285
267
|
|
|
286
268
|
const shellMf = readText('apps/shell-super-app/module-federation.config.ts');
|
|
287
269
|
assert(shellMf.includes("name: 'shellSuperApp'"), 'Shell MF config must name the shell');
|
|
288
|
-
assert(
|
|
289
|
-
|
|
290
|
-
|
|
270
|
+
assert(
|
|
271
|
+
shellMf.includes('remoteCommerce@http://localhost:3021/mf-manifest.json'),
|
|
272
|
+
'Shell must reference commerce remote',
|
|
273
|
+
);
|
|
274
|
+
assert(
|
|
275
|
+
shellMf.includes('remoteIdentity@http://localhost:3022/mf-manifest.json'),
|
|
276
|
+
'Shell must reference identity remote',
|
|
277
|
+
);
|
|
278
|
+
assert(
|
|
279
|
+
shellMf.includes('remoteDesignSystem@http://localhost:3023/mf-manifest.json'),
|
|
280
|
+
'Shell must reference design-system remote',
|
|
281
|
+
);
|
|
291
282
|
|
|
292
283
|
const commerceMf = readText('apps/remotes/remote-commerce/module-federation.config.ts');
|
|
293
284
|
assert(commerceMf.includes("name: 'remoteCommerce'"), 'Commerce remote MF name is missing');
|
|
294
|
-
assert(commerceMf.includes('
|
|
285
|
+
assert(commerceMf.includes("'./Widget'"), 'Commerce remote must expose a widget');
|
|
295
286
|
|
|
296
287
|
const designMf = readText('apps/remotes/remote-design-system/module-federation.config.ts');
|
|
297
288
|
assert(designMf.includes("name: 'remoteDesignSystem'"), 'Design-system remote MF name is missing');
|
|
298
|
-
assert(designMf.includes('
|
|
299
|
-
assert(designMf.includes('
|
|
289
|
+
assert(designMf.includes("'./Button'"), 'Design-system remote must expose Button');
|
|
290
|
+
assert(designMf.includes("'./tokens'"), 'Design-system remote must expose tokens');
|
|
300
291
|
|
|
301
292
|
const servicePackage = readJson('services/service-recommendations-effect/package.json');
|
|
302
293
|
assert(
|
|
303
294
|
servicePackage.dependencies?.['@modern-js/runtime'] ===
|
|
304
295
|
expectedModernDependency('@modern-js/runtime'),
|
|
305
|
-
`Effect service must use @modern-js/runtime through ${expectedModernDependency(
|
|
296
|
+
`Effect service must use @modern-js/runtime through ${expectedModernDependency(
|
|
297
|
+
'@modern-js/runtime',
|
|
298
|
+
)}`,
|
|
306
299
|
);
|
|
307
300
|
assert(
|
|
308
301
|
servicePackage.devDependencies?.['@modern-js/app-tools'] ===
|
|
309
302
|
expectedModernDependency('@modern-js/app-tools'),
|
|
310
|
-
`Effect service must use @modern-js/app-tools through ${expectedModernDependency(
|
|
303
|
+
`Effect service must use @modern-js/app-tools through ${expectedModernDependency(
|
|
304
|
+
'@modern-js/app-tools',
|
|
305
|
+
)}`,
|
|
311
306
|
);
|
|
312
307
|
assert(
|
|
313
308
|
servicePackage.devDependencies?.['@modern-js/plugin-bff'] ===
|
|
314
309
|
expectedModernDependency('@modern-js/plugin-bff'),
|
|
315
|
-
`Effect service must use @modern-js/plugin-bff through ${expectedModernDependency(
|
|
310
|
+
`Effect service must use @modern-js/plugin-bff through ${expectedModernDependency(
|
|
311
|
+
'@modern-js/plugin-bff',
|
|
312
|
+
)}`,
|
|
316
313
|
);
|
|
317
314
|
assert(
|
|
318
|
-
servicePackage.dependencies?.[`@${packageScope}/shared-effect-api`] ===
|
|
319
|
-
'workspace:*',
|
|
315
|
+
servicePackage.dependencies?.[`@${packageScope}/shared-effect-api`] === 'workspace:*',
|
|
320
316
|
'Effect service must link generated shared Effect API through workspace:*',
|
|
321
317
|
);
|
|
322
318
|
|
|
323
319
|
const serviceConfig = readText('services/service-recommendations-effect/modern.config.ts');
|
|
324
|
-
assert(
|
|
320
|
+
assert(
|
|
321
|
+
serviceConfig.includes("runtimeFramework: 'effect'"),
|
|
322
|
+
'Effect service must use Effect runtime',
|
|
323
|
+
);
|
|
325
324
|
assert(serviceConfig.includes('bffPlugin()'), 'Effect service must enable bffPlugin');
|
|
326
325
|
|
|
327
326
|
const serviceEntry = readText('services/service-recommendations-effect/api/effect/index.ts');
|
|
328
|
-
assert(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
assert(
|
|
327
|
+
assert(
|
|
328
|
+
serviceEntry.includes('defineEffectBff'),
|
|
329
|
+
'Effect service must expose defineEffectBff placeholder',
|
|
330
|
+
);
|
|
331
|
+
assert(
|
|
332
|
+
serviceEntry.includes('recommendationsEffectApi'),
|
|
333
|
+
'Effect service must use shared recommendations API',
|
|
334
|
+
);
|
|
335
|
+
assert(
|
|
336
|
+
readText('services/service-recommendations-effect/shared/effect/api.ts').includes('HttpApi.make'),
|
|
337
|
+
'Effect shared API placeholder must define HttpApi',
|
|
338
|
+
);
|
|
333
339
|
|
|
334
340
|
const topology = readJson('topology/reference-topology.json');
|
|
335
341
|
assert(topology.preset === 'presetUltramodern', 'Topology must reference presetUltramodern');
|
|
@@ -338,27 +344,26 @@ assert(topology.shell?.remoteRefs?.length === 3, 'Topology shell must reference
|
|
|
338
344
|
assert(topology.remotes?.length === 3, 'Topology must contain three remotes');
|
|
339
345
|
assert(
|
|
340
346
|
topology.remotes.some(
|
|
341
|
-
remote =>
|
|
342
|
-
remote.id === 'remote-design-system' &&
|
|
343
|
-
remote.kind === 'horizontal-design-system',
|
|
347
|
+
(remote) => remote.id === 'remote-design-system' && remote.kind === 'horizontal-design-system',
|
|
344
348
|
),
|
|
345
349
|
'Topology must contain the horizontal design-system remote',
|
|
346
350
|
);
|
|
347
|
-
assert(
|
|
351
|
+
assert(
|
|
352
|
+
topology.effectServices?.[0]?.runtime === 'effect',
|
|
353
|
+
'Topology must contain an Effect service',
|
|
354
|
+
);
|
|
348
355
|
assert(topology.sharedPackages?.length === 3, 'Topology must contain shared package placeholders');
|
|
349
356
|
|
|
350
357
|
const ownership = readJson('topology/ownership.json');
|
|
351
358
|
assert(
|
|
352
359
|
ownership.owners?.some(
|
|
353
|
-
owner =>
|
|
354
|
-
owner.id === 'remote-commerce' &&
|
|
355
|
-
owner.ownership?.team === 'commerce-experience',
|
|
360
|
+
(owner) => owner.id === 'remote-commerce' && owner.ownership?.team === 'commerce-experience',
|
|
356
361
|
),
|
|
357
362
|
'Ownership metadata must retain commerce owner',
|
|
358
363
|
);
|
|
359
364
|
assert(
|
|
360
365
|
ownership.owners?.some(
|
|
361
|
-
owner =>
|
|
366
|
+
(owner) =>
|
|
362
367
|
owner.id === 'service-recommendations-effect' &&
|
|
363
368
|
owner.package === `@${packageScope}/service-recommendations-effect`,
|
|
364
369
|
),
|
|
@@ -379,18 +384,15 @@ assert(
|
|
|
379
384
|
'Template manifest must retain the Rstack agent skills commit',
|
|
380
385
|
);
|
|
381
386
|
assert(
|
|
382
|
-
baselineAgentSkills.every(skillName =>
|
|
383
|
-
manifest.agentSkills?.baseline?.includes(skillName),
|
|
384
|
-
),
|
|
387
|
+
baselineAgentSkills.every((skillName) => manifest.agentSkills?.baseline?.includes(skillName)),
|
|
385
388
|
'Template manifest must list every baseline agent skill',
|
|
386
389
|
);
|
|
387
390
|
assert(
|
|
388
|
-
manifest.agentSkills?.privateSource?.repository ===
|
|
389
|
-
'https://github.com/TechsioCZ/skills',
|
|
391
|
+
manifest.agentSkills?.privateSource?.repository === 'https://github.com/TechsioCZ/skills',
|
|
390
392
|
'Template manifest must retain the private TechsioCZ skill source',
|
|
391
393
|
);
|
|
392
394
|
assert(
|
|
393
|
-
privateAgentSkills.every(skillName =>
|
|
395
|
+
privateAgentSkills.every((skillName) =>
|
|
394
396
|
manifest.agentSkills?.privateSource?.baseline?.includes(skillName),
|
|
395
397
|
),
|
|
396
398
|
'Template manifest must list every private agent skill allowlist entry',
|