@bleedingdev/modern-js-create 3.2.0-ultramodern.2 → 3.2.0-ultramodern.21
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/README.md +12 -0
- package/dist/index.js +676 -198
- package/package.json +5 -2
- package/template/.agents/skills-lock.json +34 -0
- package/template/.github/renovate.json +53 -0
- package/template/.github/workflows/ultramodern-gates.yml.handlebars +26 -4
- package/template/AGENTS.md +27 -0
- package/template/README.md +7 -3
- package/template/api/effect/index.ts.handlebars +7 -45
- 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 +44 -23
- package/template/oxfmt.config.ts +8 -0
- package/template/oxlint.config.ts +12 -0
- package/template/package.json.handlebars +56 -27
- package/template/pnpm-workspace.yaml +24 -0
- package/template/rstest.config.mts +7 -0
- package/template/scripts/bootstrap-agent-skills.mjs +95 -0
- package/template/scripts/check-i18n-strings.mjs +83 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +350 -16
- package/template/shared/effect/api.ts.handlebars +1 -2
- package/template/src/modern-app-env.d.ts +2 -0
- package/template/src/modern.runtime.ts.handlebars +17 -3
- package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
- package/template/src/routes/index.css.handlebars +14 -3
- package/template/src/routes/layout.tsx.handlebars +1 -1
- package/template/tests/tsconfig.json +7 -0
- package/template/tests/ultramodern.contract.test.ts +67 -0
- package/template/tsconfig.json +106 -2
- package/template-workspace/.agents/agent-reference-repos.json +24 -0
- package/template-workspace/.agents/rstackjs-agent-skills-LICENSE +21 -0
- package/template-workspace/.agents/skills/rsbuild-best-practices/SKILL.md +57 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/SKILL.md +96 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/command-map.md +113 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/common-analysis-patterns.md +190 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +88 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +138 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +71 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor.md +39 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +103 -0
- package/template-workspace/.agents/skills/rslib-best-practices/SKILL.md +58 -0
- package/template-workspace/.agents/skills/rslib-modern-package/SKILL.md +173 -0
- package/template-workspace/.agents/skills/rspack-best-practices/SKILL.md +70 -0
- package/template-workspace/.agents/skills/rspack-tracing/SKILL.md +75 -0
- package/template-workspace/.agents/skills/rspack-tracing/references/bottlenecks.md +47 -0
- package/template-workspace/.agents/skills/rspack-tracing/references/tracing-guide.md +38 -0
- package/template-workspace/.agents/skills/rspack-tracing/scripts/analyze_trace.js +184 -0
- package/template-workspace/.agents/skills/rstest-best-practices/SKILL.md +133 -0
- package/template-workspace/.agents/skills-lock.json +95 -0
- package/template-workspace/.github/renovate.json +29 -0
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +52 -0
- package/template-workspace/.gitignore.handlebars +5 -0
- package/template-workspace/AGENTS.md +61 -0
- package/template-workspace/README.md.handlebars +11 -1
- package/template-workspace/oxfmt.config.ts +16 -0
- package/template-workspace/oxlint.config.ts +19 -0
- package/template-workspace/pnpm-workspace.yaml +24 -6
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +95 -0
- package/template-workspace/scripts/check-i18n-strings.mjs +83 -0
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +364 -0
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +405 -59
- package/template/biome.json +0 -41
- package/template/src/routes/page.tsx.handlebars +0 -119
package/dist/index.js
CHANGED
|
@@ -485,7 +485,7 @@ const EN_LOCALE = {
|
|
|
485
485
|
example7: ' create my-app --bff',
|
|
486
486
|
example8: ' create my-app --router tanstack --bff-runtime effect',
|
|
487
487
|
example9: ' create my-app --router tanstack --bff-runtime effect --workspace',
|
|
488
|
-
example10: ' create my-super-app
|
|
488
|
+
example10: ' pnpm dlx @bleedingdev/modern-js-create my-super-app',
|
|
489
489
|
moreInfo: '📚 Learn more: https://modernjs.dev'
|
|
490
490
|
},
|
|
491
491
|
version: {
|
|
@@ -556,13 +556,39 @@ const ultramodern_workspace_dirname = node_path.dirname(fileURLToPath(import.met
|
|
|
556
556
|
const workspaceTemplateDir = node_path.resolve(ultramodern_workspace_dirname, '..', 'template-workspace');
|
|
557
557
|
const TANSTACK_ROUTER_VERSION = '1.170.1';
|
|
558
558
|
const MODULE_FEDERATION_VERSION = '2.4.0';
|
|
559
|
-
const
|
|
559
|
+
const ZEPHYR_MODERNJS_PLUGIN_VERSION = '1.1.1';
|
|
560
|
+
const EFFECT_TSGO_VERSION = '0.7.3';
|
|
561
|
+
const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260518.1';
|
|
562
|
+
const OXLINT_VERSION = '1.65.0';
|
|
563
|
+
const OXFMT_VERSION = '0.50.0';
|
|
564
|
+
const ULTRACITE_VERSION = '7.7.0';
|
|
565
|
+
const I18NEXT_VERSION = '26.2.0';
|
|
560
566
|
const REACT_VERSION = '^19.2.6';
|
|
561
567
|
const REACT_DOM_VERSION = '^19.2.6';
|
|
568
|
+
const REACT_I18NEXT_VERSION = '17.0.8';
|
|
562
569
|
const WORKSPACE_PACKAGE_VERSION = 'workspace:*';
|
|
570
|
+
const RSTACK_AGENT_SKILLS_COMMIT = '61c948b42512e223bad44b83af4080eba48b2677';
|
|
571
|
+
const baselineAgentSkills = [
|
|
572
|
+
'rsbuild-best-practices',
|
|
573
|
+
'rspack-best-practices',
|
|
574
|
+
'rspack-tracing',
|
|
575
|
+
'rsdoctor-analysis',
|
|
576
|
+
'rslib-best-practices',
|
|
577
|
+
'rslib-modern-package',
|
|
578
|
+
'rstest-best-practices'
|
|
579
|
+
];
|
|
580
|
+
const privateAgentSkills = [
|
|
581
|
+
'plan-graph',
|
|
582
|
+
'dag',
|
|
583
|
+
'subagent-graph',
|
|
584
|
+
'helm',
|
|
585
|
+
'debugger-mode'
|
|
586
|
+
];
|
|
587
|
+
const effectTsgoTypecheckCommand = "node -e \"const fs = require('node:fs'); const { execFileSync, spawnSync } = require('node:child_process'); const bin = execFileSync('effect-tsgo', ['get-exe-path'], { encoding: 'utf8' }).trim(); if (process.platform !== 'win32') fs.chmodSync(bin, 0o755); const result = spawnSync(bin, ['--noEmit', '-p', 'tsconfig.json'], { stdio: 'inherit' }); process.exit(result.status ?? 1);\"";
|
|
563
588
|
const modernPackageNames = [
|
|
564
589
|
'@modern-js/app-tools',
|
|
565
590
|
'@modern-js/plugin-bff',
|
|
591
|
+
'@modern-js/plugin-i18n',
|
|
566
592
|
'@modern-js/plugin-tanstack',
|
|
567
593
|
'@modern-js/runtime'
|
|
568
594
|
];
|
|
@@ -609,7 +635,7 @@ const remoteApps = [
|
|
|
609
635
|
mfName: 'remoteCommerce',
|
|
610
636
|
exposes: {
|
|
611
637
|
'./Route': './src/remote-entry.tsx',
|
|
612
|
-
'./Widget': './src/components/
|
|
638
|
+
'./Widget': './src/components/commerce-widget.tsx'
|
|
613
639
|
},
|
|
614
640
|
ownership: {
|
|
615
641
|
team: 'commerce-experience',
|
|
@@ -638,7 +664,7 @@ const remoteApps = [
|
|
|
638
664
|
mfName: 'remoteIdentity',
|
|
639
665
|
exposes: {
|
|
640
666
|
'./Route': './src/remote-entry.tsx',
|
|
641
|
-
'./Widget': './src/components/
|
|
667
|
+
'./Widget': './src/components/identity-widget.tsx'
|
|
642
668
|
},
|
|
643
669
|
ownership: {
|
|
644
670
|
team: 'identity-platform',
|
|
@@ -666,7 +692,7 @@ const remoteApps = [
|
|
|
666
692
|
port: 3023,
|
|
667
693
|
mfName: 'remoteDesignSystem',
|
|
668
694
|
exposes: {
|
|
669
|
-
'./Button': './src/components/
|
|
695
|
+
'./Button': './src/components/button.tsx',
|
|
670
696
|
'./tokens': './src/tokens.ts'
|
|
671
697
|
},
|
|
672
698
|
ownership: {
|
|
@@ -706,6 +732,81 @@ const effectService = {
|
|
|
706
732
|
}
|
|
707
733
|
}
|
|
708
734
|
};
|
|
735
|
+
const effectDiagnostics = [
|
|
736
|
+
'anyUnknownInErrorContext',
|
|
737
|
+
'classSelfMismatch',
|
|
738
|
+
'duplicatePackage',
|
|
739
|
+
'effectFnImplicitAny',
|
|
740
|
+
'floatingEffect',
|
|
741
|
+
'genericEffectServices',
|
|
742
|
+
'missingEffectContext',
|
|
743
|
+
'missingEffectError',
|
|
744
|
+
'missingLayerContext',
|
|
745
|
+
'missingReturnYieldStar',
|
|
746
|
+
'missingStarInYieldEffectGen',
|
|
747
|
+
'nonObjectEffectServiceType',
|
|
748
|
+
'outdatedApi',
|
|
749
|
+
'overriddenSchemaConstructor',
|
|
750
|
+
'catchUnfailableEffect',
|
|
751
|
+
'effectFnIife',
|
|
752
|
+
'effectGenUsesAdapter',
|
|
753
|
+
'effectInFailure',
|
|
754
|
+
'effectInVoidSuccess',
|
|
755
|
+
'globalErrorInEffectCatch',
|
|
756
|
+
'globalErrorInEffectFailure',
|
|
757
|
+
'layerMergeAllWithDependencies',
|
|
758
|
+
'lazyPromiseInEffectSync',
|
|
759
|
+
'leakingRequirements',
|
|
760
|
+
'multipleEffectProvide',
|
|
761
|
+
'returnEffectInGen',
|
|
762
|
+
'runEffectInsideEffect',
|
|
763
|
+
'schemaSyncInEffect',
|
|
764
|
+
'scopeInLayerEffect',
|
|
765
|
+
'strictEffectProvide',
|
|
766
|
+
'tryCatchInEffectGen',
|
|
767
|
+
'unknownInEffectCatch',
|
|
768
|
+
'asyncFunction',
|
|
769
|
+
'cryptoRandomUUID',
|
|
770
|
+
'cryptoRandomUUIDInEffect',
|
|
771
|
+
'extendsNativeError',
|
|
772
|
+
'globalConsole',
|
|
773
|
+
'globalConsoleInEffect',
|
|
774
|
+
'globalDate',
|
|
775
|
+
'globalDateInEffect',
|
|
776
|
+
'globalFetch',
|
|
777
|
+
'globalFetchInEffect',
|
|
778
|
+
'globalRandom',
|
|
779
|
+
'globalRandomInEffect',
|
|
780
|
+
'globalTimers',
|
|
781
|
+
'globalTimersInEffect',
|
|
782
|
+
'instanceOfSchema',
|
|
783
|
+
'newPromise',
|
|
784
|
+
'nodeBuiltinImport',
|
|
785
|
+
'preferSchemaOverJson',
|
|
786
|
+
'processEnv',
|
|
787
|
+
'processEnvInEffect',
|
|
788
|
+
'unsafeEffectTypeAssertion',
|
|
789
|
+
'catchAllToMapError',
|
|
790
|
+
'deterministicKeys',
|
|
791
|
+
'effectDoNotation',
|
|
792
|
+
'effectFnOpportunity',
|
|
793
|
+
'effectMapFlatten',
|
|
794
|
+
'effectMapVoid',
|
|
795
|
+
'effectSucceedWithVoid',
|
|
796
|
+
'missedPipeableOpportunity',
|
|
797
|
+
'missingEffectServiceDependency',
|
|
798
|
+
'nestedEffectGenYield',
|
|
799
|
+
'redundantSchemaTagIdentifier',
|
|
800
|
+
'schemaStructWithTag',
|
|
801
|
+
'schemaUnionOfLiterals',
|
|
802
|
+
'serviceNotAsClass',
|
|
803
|
+
'strictBooleanExpressions',
|
|
804
|
+
'unnecessaryArrowBlock',
|
|
805
|
+
'unnecessaryEffectGen',
|
|
806
|
+
'unnecessaryFailYieldableError',
|
|
807
|
+
'unnecessaryPipe',
|
|
808
|
+
'unnecessaryPipeChain'
|
|
809
|
+
];
|
|
709
810
|
const sharedPackages = [
|
|
710
811
|
{
|
|
711
812
|
id: 'shared-contracts',
|
|
@@ -820,24 +921,29 @@ function modernPackageSpecifier(packageName, packageSource) {
|
|
|
820
921
|
}
|
|
821
922
|
function appDependencies(scope, packageSource) {
|
|
822
923
|
return {
|
|
924
|
+
'@modern-js/plugin-i18n': modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
|
|
823
925
|
'@modern-js/plugin-tanstack': modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
|
|
824
926
|
'@modern-js/runtime': modernPackageSpecifier('@modern-js/runtime', packageSource),
|
|
825
927
|
'@module-federation/modern-js-v3': MODULE_FEDERATION_VERSION,
|
|
826
928
|
'@module-federation/runtime': MODULE_FEDERATION_VERSION,
|
|
827
929
|
'@tanstack/react-router': TANSTACK_ROUTER_VERSION,
|
|
930
|
+
'zephyr-modernjs-plugin': ZEPHYR_MODERNJS_PLUGIN_VERSION,
|
|
828
931
|
[ultramodern_workspace_packageName(scope, 'shared-contracts')]: WORKSPACE_PACKAGE_VERSION,
|
|
829
932
|
[ultramodern_workspace_packageName(scope, 'shared-design-tokens')]: WORKSPACE_PACKAGE_VERSION,
|
|
933
|
+
i18next: I18NEXT_VERSION,
|
|
830
934
|
react: REACT_VERSION,
|
|
831
|
-
'react-dom': REACT_DOM_VERSION
|
|
935
|
+
'react-dom': REACT_DOM_VERSION,
|
|
936
|
+
'react-i18next': REACT_I18NEXT_VERSION
|
|
832
937
|
};
|
|
833
938
|
}
|
|
834
939
|
function appDevDependencies(packageSource) {
|
|
835
940
|
return {
|
|
836
941
|
'@modern-js/app-tools': modernPackageSpecifier('@modern-js/app-tools', packageSource),
|
|
942
|
+
'@effect/tsgo': EFFECT_TSGO_VERSION,
|
|
943
|
+
"@typescript/native-preview": TYPESCRIPT_NATIVE_PREVIEW_VERSION,
|
|
837
944
|
'@types/node': '^20',
|
|
838
945
|
'@types/react': '^19.1.8',
|
|
839
|
-
'@types/react-dom': '^19.1.6'
|
|
840
|
-
typescript: TYPESCRIPT_VERSION
|
|
946
|
+
'@types/react-dom': '^19.1.6'
|
|
841
947
|
};
|
|
842
948
|
}
|
|
843
949
|
function createRootPackageJson(scope, packageSource) {
|
|
@@ -845,6 +951,8 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
845
951
|
private: true,
|
|
846
952
|
name: scope,
|
|
847
953
|
version: '0.1.0',
|
|
954
|
+
type: 'module',
|
|
955
|
+
packageManager: 'pnpm@11.1.2',
|
|
848
956
|
scripts: {
|
|
849
957
|
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`,
|
|
850
958
|
'dev:shell': `pnpm --filter ${ultramodern_workspace_packageName(scope, shellApp.packageSuffix)} dev`,
|
|
@@ -853,13 +961,23 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
853
961
|
'dev:design-system': `pnpm --filter ${ultramodern_workspace_packageName(scope, 'remote-design-system')} dev`,
|
|
854
962
|
'dev:recommendations': `pnpm --filter ${ultramodern_workspace_packageName(scope, effectService.packageSuffix)} dev`,
|
|
855
963
|
build: 'pnpm -r --filter ./apps/** --filter ./services/** build',
|
|
856
|
-
|
|
964
|
+
format: 'oxfmt .',
|
|
965
|
+
'format:check': 'oxfmt --check .',
|
|
966
|
+
'i18n:check': "node ./scripts/check-i18n-strings.mjs",
|
|
967
|
+
lint: 'oxlint .',
|
|
968
|
+
'lint:fix': 'oxlint . --fix',
|
|
969
|
+
typecheck: `pnpm -r --filter "@${scope}/*" typecheck`,
|
|
970
|
+
'skills:install': "node ./scripts/bootstrap-agent-skills.mjs",
|
|
971
|
+
'skills:check': "node ./scripts/bootstrap-agent-skills.mjs --check",
|
|
972
|
+
'agents:refs:install': "node ./scripts/setup-agent-reference-repos.mjs",
|
|
973
|
+
'agents:refs:check': "node ./scripts/setup-agent-reference-repos.mjs --check",
|
|
857
974
|
'ultramodern:check': "node ./scripts/validate-ultramodern-workspace.mjs",
|
|
858
|
-
|
|
975
|
+
postinstall: "node ./scripts/setup-agent-reference-repos.mjs",
|
|
976
|
+
check: 'pnpm format:check && pnpm lint && pnpm typecheck && pnpm i18n:check && pnpm skills:check && pnpm ultramodern:check'
|
|
859
977
|
},
|
|
860
978
|
engines: {
|
|
861
979
|
node: '>=20',
|
|
862
|
-
pnpm: '>=
|
|
980
|
+
pnpm: '>=11.0.0'
|
|
863
981
|
},
|
|
864
982
|
workspaces: [
|
|
865
983
|
'apps/*',
|
|
@@ -878,35 +996,56 @@ function createRootPackageJson(scope, packageSource) {
|
|
|
878
996
|
}
|
|
879
997
|
},
|
|
880
998
|
devDependencies: {
|
|
881
|
-
'@
|
|
882
|
-
typescript:
|
|
999
|
+
'@effect/tsgo': EFFECT_TSGO_VERSION,
|
|
1000
|
+
"@typescript/native-preview": TYPESCRIPT_NATIVE_PREVIEW_VERSION,
|
|
1001
|
+
oxlint: OXLINT_VERSION,
|
|
1002
|
+
oxfmt: OXFMT_VERSION,
|
|
1003
|
+
ultracite: ULTRACITE_VERSION
|
|
883
1004
|
}
|
|
884
1005
|
};
|
|
885
1006
|
}
|
|
886
|
-
function createTsConfigBase(
|
|
1007
|
+
function createTsConfigBase() {
|
|
887
1008
|
return {
|
|
888
1009
|
compilerOptions: {
|
|
889
|
-
target: '
|
|
1010
|
+
target: 'ESNext',
|
|
890
1011
|
lib: [
|
|
1012
|
+
'ESNext',
|
|
891
1013
|
'DOM',
|
|
892
|
-
'DOM.Iterable'
|
|
893
|
-
'ES2022'
|
|
1014
|
+
'DOM.Iterable'
|
|
894
1015
|
],
|
|
895
|
-
module: '
|
|
1016
|
+
module: 'preserve',
|
|
896
1017
|
moduleResolution: 'Bundler',
|
|
1018
|
+
moduleDetection: 'force',
|
|
897
1019
|
jsx: 'preserve',
|
|
1020
|
+
isolatedModules: true,
|
|
1021
|
+
verbatimModuleSyntax: true,
|
|
898
1022
|
strict: true,
|
|
899
1023
|
noEmit: true,
|
|
1024
|
+
allowJs: true,
|
|
900
1025
|
esModuleInterop: true,
|
|
1026
|
+
noUncheckedIndexedAccess: true,
|
|
1027
|
+
exactOptionalPropertyTypes: true,
|
|
1028
|
+
noImplicitOverride: true,
|
|
1029
|
+
noFallthroughCasesInSwitch: true,
|
|
1030
|
+
noPropertyAccessFromIndexSignature: true,
|
|
1031
|
+
noImplicitReturns: true,
|
|
901
1032
|
skipLibCheck: true,
|
|
902
1033
|
resolveJsonModule: true,
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1034
|
+
plugins: [
|
|
1035
|
+
{
|
|
1036
|
+
name: '@effect/language-service',
|
|
1037
|
+
diagnostics: true,
|
|
1038
|
+
includeSuggestionsInTsc: true,
|
|
1039
|
+
ignoreEffectSuggestionsInTscExitCode: false,
|
|
1040
|
+
ignoreEffectWarningsInTscExitCode: false,
|
|
1041
|
+
ignoreEffectErrorsInTscExitCode: false,
|
|
1042
|
+
skipDisabledOptimization: true,
|
|
1043
|
+
diagnosticSeverity: Object.fromEntries(effectDiagnostics.map((name)=>[
|
|
1044
|
+
name,
|
|
1045
|
+
'error'
|
|
1046
|
+
]))
|
|
1047
|
+
}
|
|
1048
|
+
]
|
|
910
1049
|
}
|
|
911
1050
|
};
|
|
912
1051
|
}
|
|
@@ -919,20 +1058,6 @@ function createPackageTsConfig(packageDir, includeApi = false) {
|
|
|
919
1058
|
if (includeApi) include.push('api', 'shared');
|
|
920
1059
|
return {
|
|
921
1060
|
extends: `${relativeRootFor(packageDir)}/tsconfig.base.json`,
|
|
922
|
-
compilerOptions: {
|
|
923
|
-
baseUrl: '.',
|
|
924
|
-
paths: {
|
|
925
|
-
'@/*': [
|
|
926
|
-
'./src/*'
|
|
927
|
-
],
|
|
928
|
-
'@api/*': [
|
|
929
|
-
'./api/*'
|
|
930
|
-
],
|
|
931
|
-
'@shared/*': [
|
|
932
|
-
'./shared/*'
|
|
933
|
-
]
|
|
934
|
-
}
|
|
935
|
-
},
|
|
936
1061
|
include
|
|
937
1062
|
};
|
|
938
1063
|
}
|
|
@@ -945,7 +1070,7 @@ function createAppPackage(scope, app, packageSource) {
|
|
|
945
1070
|
dev: 'modern dev',
|
|
946
1071
|
build: 'modern build',
|
|
947
1072
|
serve: 'modern serve',
|
|
948
|
-
typecheck:
|
|
1073
|
+
typecheck: effectTsgoTypecheckCommand
|
|
949
1074
|
},
|
|
950
1075
|
modernjs: {
|
|
951
1076
|
preset: 'presetUltramodern',
|
|
@@ -966,7 +1091,7 @@ function createServicePackage(scope, packageSource) {
|
|
|
966
1091
|
dev: 'modern dev',
|
|
967
1092
|
build: 'modern build',
|
|
968
1093
|
serve: 'modern serve',
|
|
969
|
-
typecheck:
|
|
1094
|
+
typecheck: effectTsgoTypecheckCommand
|
|
970
1095
|
},
|
|
971
1096
|
modernjs: {
|
|
972
1097
|
preset: 'presetUltramodern',
|
|
@@ -983,10 +1108,11 @@ function createServicePackage(scope, packageSource) {
|
|
|
983
1108
|
devDependencies: {
|
|
984
1109
|
'@modern-js/app-tools': modernPackageSpecifier('@modern-js/app-tools', packageSource),
|
|
985
1110
|
'@modern-js/plugin-bff': modernPackageSpecifier('@modern-js/plugin-bff', packageSource),
|
|
1111
|
+
'@effect/tsgo': EFFECT_TSGO_VERSION,
|
|
1112
|
+
"@typescript/native-preview": TYPESCRIPT_NATIVE_PREVIEW_VERSION,
|
|
986
1113
|
'@types/node': '^20',
|
|
987
1114
|
'@types/react': '^19.1.8',
|
|
988
|
-
'@types/react-dom': '^19.1.6'
|
|
989
|
-
typescript: TYPESCRIPT_VERSION
|
|
1115
|
+
'@types/react-dom': '^19.1.6'
|
|
990
1116
|
}
|
|
991
1117
|
};
|
|
992
1118
|
}
|
|
@@ -1001,46 +1127,84 @@ function createSharedPackage(scope, id, description) {
|
|
|
1001
1127
|
'.': './src/index.ts'
|
|
1002
1128
|
},
|
|
1003
1129
|
scripts: {
|
|
1004
|
-
typecheck:
|
|
1130
|
+
typecheck: effectTsgoTypecheckCommand
|
|
1005
1131
|
},
|
|
1006
1132
|
devDependencies: {
|
|
1007
|
-
|
|
1133
|
+
'@effect/tsgo': EFFECT_TSGO_VERSION,
|
|
1134
|
+
"@typescript/native-preview": TYPESCRIPT_NATIVE_PREVIEW_VERSION
|
|
1008
1135
|
}
|
|
1009
1136
|
};
|
|
1010
1137
|
}
|
|
1011
1138
|
function createAppModernConfig(app) {
|
|
1012
|
-
return
|
|
1139
|
+
return `// @effect-diagnostics processEnv:off
|
|
1140
|
+
import { appTools, defineConfig, presetUltramodern } from '@modern-js/app-tools';
|
|
1141
|
+
import { i18nPlugin } from '@modern-js/plugin-i18n';
|
|
1013
1142
|
import { tanstackRouterPlugin } from '@modern-js/plugin-tanstack';
|
|
1014
1143
|
import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
|
|
1144
|
+
import { withZephyr } from 'zephyr-modernjs-plugin';
|
|
1015
1145
|
|
|
1016
1146
|
const appId = '${app.id}';
|
|
1017
|
-
const port = Number(process.env
|
|
1147
|
+
const port = Number(process.env['${app.portEnv}'] ?? ${app.port});
|
|
1148
|
+
const configuredSiteUrl = process.env['MODERN_PUBLIC_SITE_URL'];
|
|
1149
|
+
const hasConfiguredSiteUrl = typeof configuredSiteUrl === 'string' && configuredSiteUrl.length > 0;
|
|
1150
|
+
const isProductionBuild =
|
|
1151
|
+
process.env['NODE_ENV'] === 'production' || process.argv.includes('build');
|
|
1152
|
+
|
|
1153
|
+
if (isProductionBuild && !hasConfiguredSiteUrl) {
|
|
1154
|
+
throw new Error(
|
|
1155
|
+
'MODERN_PUBLIC_SITE_URL must be set for production builds so canonical and hreflang URLs use the deployed origin.',
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
const siteUrl = hasConfiguredSiteUrl ? configuredSiteUrl : \`http://localhost:\${port}\`;
|
|
1018
1160
|
|
|
1019
1161
|
export default defineConfig(
|
|
1020
1162
|
presetUltramodern(
|
|
1021
1163
|
{
|
|
1022
|
-
server: {
|
|
1023
|
-
port,
|
|
1024
|
-
ssr: {
|
|
1025
|
-
mode: 'string',
|
|
1026
|
-
moduleFederationAppSSR: true,
|
|
1027
|
-
},
|
|
1028
|
-
},
|
|
1029
1164
|
output: {
|
|
1030
|
-
polyfill: 'off',
|
|
1031
1165
|
disableTsChecker: true,
|
|
1166
|
+
distPath: {
|
|
1167
|
+
html: './',
|
|
1168
|
+
},
|
|
1169
|
+
polyfill: 'off',
|
|
1032
1170
|
splitRouteChunks: false,
|
|
1033
1171
|
},
|
|
1172
|
+
html: {
|
|
1173
|
+
outputStructure: 'flat',
|
|
1174
|
+
},
|
|
1034
1175
|
plugins: [
|
|
1035
|
-
appTools(
|
|
1176
|
+
appTools({
|
|
1177
|
+
bundler: 'rspack',
|
|
1178
|
+
}),
|
|
1179
|
+
i18nPlugin({
|
|
1180
|
+
localeDetection: {
|
|
1181
|
+
fallbackLanguage: 'en',
|
|
1182
|
+
languages: ['en', 'cs'],
|
|
1183
|
+
localePathRedirect: true,
|
|
1184
|
+
},
|
|
1185
|
+
}),
|
|
1036
1186
|
tanstackRouterPlugin(),
|
|
1037
1187
|
moduleFederationPlugin(),
|
|
1188
|
+
withZephyr(),
|
|
1038
1189
|
],
|
|
1190
|
+
server: {
|
|
1191
|
+
port,
|
|
1192
|
+
ssr: {
|
|
1193
|
+
mode: 'string',
|
|
1194
|
+
moduleFederationAppSSR: true,
|
|
1195
|
+
},
|
|
1196
|
+
},
|
|
1197
|
+
source: {
|
|
1198
|
+
mainEntryName: 'index',
|
|
1199
|
+
globalVars: {
|
|
1200
|
+
ULTRAMODERN_SITE_URL: siteUrl,
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1039
1203
|
},
|
|
1040
1204
|
{
|
|
1041
1205
|
appId,
|
|
1042
|
-
enableModuleFederationSSR: true,
|
|
1043
1206
|
enableBffRequestId: true,
|
|
1207
|
+
enableModuleFederationSSR: true,
|
|
1044
1208
|
enableTelemetryExporters: true,
|
|
1045
1209
|
telemetryFailLoudStartup: false,
|
|
1046
1210
|
},
|
|
@@ -1048,129 +1212,122 @@ export default defineConfig(
|
|
|
1048
1212
|
);
|
|
1049
1213
|
`;
|
|
1050
1214
|
}
|
|
1215
|
+
function createSharedModuleFederationConfig() {
|
|
1216
|
+
return ` shared: {
|
|
1217
|
+
'@modern-js/runtime': {
|
|
1218
|
+
requiredVersion: runtimeVersion,
|
|
1219
|
+
singleton: true,
|
|
1220
|
+
treeShaking: false,
|
|
1221
|
+
},
|
|
1222
|
+
'@tanstack/react-router': {
|
|
1223
|
+
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1224
|
+
singleton: true,
|
|
1225
|
+
treeShaking: false,
|
|
1226
|
+
},
|
|
1227
|
+
i18next: {
|
|
1228
|
+
requiredVersion: dependencies.i18next,
|
|
1229
|
+
singleton: true,
|
|
1230
|
+
treeShaking: false,
|
|
1231
|
+
},
|
|
1232
|
+
react: {
|
|
1233
|
+
requiredVersion: reactVersion,
|
|
1234
|
+
singleton: true,
|
|
1235
|
+
treeShaking: false,
|
|
1236
|
+
},
|
|
1237
|
+
'react-dom': {
|
|
1238
|
+
requiredVersion: reactDomVersion,
|
|
1239
|
+
singleton: true,
|
|
1240
|
+
treeShaking: false,
|
|
1241
|
+
},
|
|
1242
|
+
'react-i18next': {
|
|
1243
|
+
requiredVersion: dependencies['react-i18next'],
|
|
1244
|
+
singleton: true,
|
|
1245
|
+
treeShaking: false,
|
|
1246
|
+
},
|
|
1247
|
+
}`;
|
|
1248
|
+
}
|
|
1249
|
+
function formatTsObjectLiteral(value) {
|
|
1250
|
+
const entries = Object.entries(value).sort(([left], [right])=>left.localeCompare(right));
|
|
1251
|
+
if (0 === entries.length) return '{}';
|
|
1252
|
+
return `{
|
|
1253
|
+
${entries.map(([key, entryValue])=>` '${key}': '${entryValue}',`).join('\n')}
|
|
1254
|
+
}`;
|
|
1255
|
+
}
|
|
1051
1256
|
function createShellModuleFederationConfig() {
|
|
1052
|
-
return
|
|
1257
|
+
return `// @effect-diagnostics nodeBuiltinImport:off processEnv:off
|
|
1258
|
+
import { createRequire } from 'node:module';
|
|
1053
1259
|
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
1054
1260
|
import { dependencies } from './package.json';
|
|
1055
1261
|
|
|
1056
1262
|
const require = createRequire(import.meta.url);
|
|
1057
|
-
const runtimeVersion = (
|
|
1058
|
-
|
|
1059
|
-
).version;
|
|
1060
|
-
const reactVersion = (require('react/package.json') as { version: string })
|
|
1061
|
-
.version;
|
|
1062
|
-
const reactDomVersion = (
|
|
1063
|
-
require('react-dom/package.json') as { version: string }
|
|
1064
|
-
).version;
|
|
1263
|
+
const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
|
|
1264
|
+
const reactVersion = (require('react/package.json') as { version: string }).version;
|
|
1265
|
+
const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
|
|
1065
1266
|
|
|
1066
1267
|
export default createModuleFederationConfig({
|
|
1067
|
-
name: '${shellApp.mfName}',
|
|
1068
1268
|
dts: false,
|
|
1269
|
+
filename: 'remoteEntry.js',
|
|
1270
|
+
name: '${shellApp.mfName}',
|
|
1069
1271
|
remotes: {
|
|
1070
1272
|
commerce:
|
|
1071
|
-
process.env
|
|
1273
|
+
process.env['REMOTE_COMMERCE_MF_MANIFEST'] ??
|
|
1072
1274
|
'remoteCommerce@http://localhost:3021/mf-manifest.json',
|
|
1073
|
-
identity:
|
|
1074
|
-
process.env.REMOTE_IDENTITY_MF_MANIFEST ??
|
|
1075
|
-
'remoteIdentity@http://localhost:3022/mf-manifest.json',
|
|
1076
1275
|
designSystem:
|
|
1077
|
-
process.env
|
|
1276
|
+
process.env['REMOTE_DESIGN_SYSTEM_MF_MANIFEST'] ??
|
|
1078
1277
|
'remoteDesignSystem@http://localhost:3023/mf-manifest.json',
|
|
1278
|
+
identity:
|
|
1279
|
+
process.env['REMOTE_IDENTITY_MF_MANIFEST'] ??
|
|
1280
|
+
'remoteIdentity@http://localhost:3022/mf-manifest.json',
|
|
1079
1281
|
},
|
|
1080
|
-
|
|
1081
|
-
react: {
|
|
1082
|
-
singleton: true,
|
|
1083
|
-
requiredVersion: reactVersion,
|
|
1084
|
-
treeShaking: false,
|
|
1085
|
-
},
|
|
1086
|
-
'react-dom': {
|
|
1087
|
-
singleton: true,
|
|
1088
|
-
requiredVersion: reactDomVersion,
|
|
1089
|
-
treeShaking: false,
|
|
1090
|
-
},
|
|
1091
|
-
'@tanstack/react-router': {
|
|
1092
|
-
singleton: true,
|
|
1093
|
-
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1094
|
-
treeShaking: false,
|
|
1095
|
-
},
|
|
1096
|
-
'@modern-js/runtime': {
|
|
1097
|
-
singleton: true,
|
|
1098
|
-
requiredVersion: runtimeVersion,
|
|
1099
|
-
treeShaking: false,
|
|
1100
|
-
},
|
|
1101
|
-
},
|
|
1282
|
+
${createSharedModuleFederationConfig()},
|
|
1102
1283
|
});
|
|
1103
1284
|
`;
|
|
1104
1285
|
}
|
|
1105
1286
|
function createRemoteModuleFederationConfig(app) {
|
|
1106
|
-
const exposes =
|
|
1107
|
-
return
|
|
1287
|
+
const exposes = formatTsObjectLiteral(app.exposes ?? {});
|
|
1288
|
+
return `// @effect-diagnostics nodeBuiltinImport:off
|
|
1289
|
+
import { createRequire } from 'node:module';
|
|
1108
1290
|
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
|
|
1109
1291
|
import { dependencies } from './package.json';
|
|
1110
1292
|
|
|
1111
1293
|
const require = createRequire(import.meta.url);
|
|
1112
|
-
const runtimeVersion = (
|
|
1113
|
-
|
|
1114
|
-
).version;
|
|
1115
|
-
const reactVersion = (require('react/package.json') as { version: string })
|
|
1116
|
-
.version;
|
|
1117
|
-
const reactDomVersion = (
|
|
1118
|
-
require('react-dom/package.json') as { version: string }
|
|
1119
|
-
).version;
|
|
1294
|
+
const runtimeVersion = (require('@modern-js/runtime/package.json') as { version: string }).version;
|
|
1295
|
+
const reactVersion = (require('react/package.json') as { version: string }).version;
|
|
1296
|
+
const reactDomVersion = (require('react-dom/package.json') as { version: string }).version;
|
|
1120
1297
|
|
|
1121
1298
|
export default createModuleFederationConfig({
|
|
1122
|
-
name: '${app.mfName}',
|
|
1123
1299
|
dts: false,
|
|
1124
|
-
filename: 'remoteEntry.js',
|
|
1125
1300
|
exposes: ${exposes},
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
requiredVersion: reactVersion,
|
|
1130
|
-
treeShaking: false,
|
|
1131
|
-
},
|
|
1132
|
-
'react-dom': {
|
|
1133
|
-
singleton: true,
|
|
1134
|
-
requiredVersion: reactDomVersion,
|
|
1135
|
-
treeShaking: false,
|
|
1136
|
-
},
|
|
1137
|
-
'@tanstack/react-router': {
|
|
1138
|
-
singleton: true,
|
|
1139
|
-
requiredVersion: dependencies['@tanstack/react-router'],
|
|
1140
|
-
treeShaking: false,
|
|
1141
|
-
},
|
|
1142
|
-
'@modern-js/runtime': {
|
|
1143
|
-
singleton: true,
|
|
1144
|
-
requiredVersion: runtimeVersion,
|
|
1145
|
-
treeShaking: false,
|
|
1146
|
-
},
|
|
1147
|
-
},
|
|
1301
|
+
filename: 'remoteEntry.js',
|
|
1302
|
+
name: '${app.mfName}',
|
|
1303
|
+
${createSharedModuleFederationConfig()},
|
|
1148
1304
|
});
|
|
1149
1305
|
`;
|
|
1150
1306
|
}
|
|
1151
1307
|
function createServiceModernConfig() {
|
|
1152
|
-
return
|
|
1308
|
+
return `// @effect-diagnostics processEnv:off
|
|
1309
|
+
import { appTools, defineConfig, presetUltramodern } from '@modern-js/app-tools';
|
|
1153
1310
|
import { bffPlugin } from '@modern-js/plugin-bff';
|
|
1154
1311
|
|
|
1155
1312
|
const appId = '${effectService.id}';
|
|
1156
|
-
const port = Number(process.env
|
|
1313
|
+
const port = Number(process.env['${effectService.portEnv}'] ?? ${effectService.port});
|
|
1157
1314
|
|
|
1158
1315
|
export default defineConfig(
|
|
1159
1316
|
presetUltramodern(
|
|
1160
1317
|
{
|
|
1161
|
-
server: {
|
|
1162
|
-
port,
|
|
1163
|
-
},
|
|
1164
1318
|
bff: {
|
|
1165
|
-
prefix: '/recommendations-api',
|
|
1166
|
-
runtimeFramework: 'effect',
|
|
1167
1319
|
effect: {
|
|
1168
1320
|
openapi: {
|
|
1169
1321
|
path: '/openapi.json',
|
|
1170
1322
|
},
|
|
1171
1323
|
},
|
|
1324
|
+
prefix: '/recommendations-api',
|
|
1325
|
+
runtimeFramework: 'effect',
|
|
1172
1326
|
},
|
|
1173
1327
|
plugins: [appTools(), bffPlugin()],
|
|
1328
|
+
server: {
|
|
1329
|
+
port,
|
|
1330
|
+
},
|
|
1174
1331
|
},
|
|
1175
1332
|
{
|
|
1176
1333
|
appId,
|
|
@@ -1182,21 +1339,135 @@ export default defineConfig(
|
|
|
1182
1339
|
);
|
|
1183
1340
|
`;
|
|
1184
1341
|
}
|
|
1342
|
+
function createAppRuntimeConfig() {
|
|
1343
|
+
return `import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
1344
|
+
import { createInstance } from 'i18next';
|
|
1345
|
+
|
|
1346
|
+
const i18nInstance = createInstance();
|
|
1347
|
+
|
|
1348
|
+
export default defineRuntimeConfig({
|
|
1349
|
+
i18n: {
|
|
1350
|
+
i18nInstance,
|
|
1351
|
+
initOptions: {
|
|
1352
|
+
defaultNS: 'translation',
|
|
1353
|
+
fallbackLng: 'en',
|
|
1354
|
+
interpolation: {
|
|
1355
|
+
escapeValue: false,
|
|
1356
|
+
},
|
|
1357
|
+
ns: ['translation'],
|
|
1358
|
+
supportedLngs: ['en', 'cs'],
|
|
1359
|
+
},
|
|
1360
|
+
},
|
|
1361
|
+
router: {
|
|
1362
|
+
framework: 'tanstack',
|
|
1363
|
+
},
|
|
1364
|
+
});
|
|
1365
|
+
`;
|
|
1366
|
+
}
|
|
1367
|
+
function createLocalizedHeadComponent(includeLocationSuffix = false) {
|
|
1368
|
+
return `const fallbackLanguage = 'en';
|
|
1369
|
+
const supportedLanguages = ['en', 'cs'] as const;
|
|
1370
|
+
type SupportedLanguage = (typeof supportedLanguages)[number];
|
|
1371
|
+
|
|
1372
|
+
const isSupportedLanguage = (value: string): value is SupportedLanguage =>
|
|
1373
|
+
supportedLanguages.includes(value as SupportedLanguage);
|
|
1374
|
+
|
|
1375
|
+
const stripLanguagePrefix = (pathname: string) => {
|
|
1376
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
1377
|
+
if (segments.length > 0 && isSupportedLanguage(segments[0] ?? '')) {
|
|
1378
|
+
segments.shift();
|
|
1379
|
+
}
|
|
1380
|
+
return \`/\${segments.join('/')}\`;
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
const localizedPath = (pathname: string, language: SupportedLanguage) => {
|
|
1384
|
+
const pathWithoutLanguage = stripLanguagePrefix(pathname);
|
|
1385
|
+
return pathWithoutLanguage === '/' ? \`/\${language}\` : \`/\${language}\${pathWithoutLanguage}\`;
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
const absoluteUrl = (pathname: string) => {
|
|
1389
|
+
const origin = ULTRAMODERN_SITE_URL.replace(/\\/+$/u, '');
|
|
1390
|
+
return \`\${origin}\${pathname}\`;
|
|
1391
|
+
};
|
|
1392
|
+
${includeLocationSuffix ? `
|
|
1393
|
+
const locationSuffix = (location: { hash?: unknown; search?: unknown; searchStr?: unknown }) => {
|
|
1394
|
+
const { hash, search, searchStr } = location;
|
|
1395
|
+
let locationSearch = '';
|
|
1396
|
+
if (typeof searchStr === 'string') {
|
|
1397
|
+
locationSearch = searchStr;
|
|
1398
|
+
} else if (typeof search === 'string') {
|
|
1399
|
+
locationSearch = search;
|
|
1400
|
+
}
|
|
1401
|
+
const locationHash = typeof hash === 'string' ? hash : '';
|
|
1402
|
+
return \`\${locationSearch}\${locationHash}\`;
|
|
1403
|
+
};
|
|
1404
|
+
` : ''}
|
|
1405
|
+
const LocalizedHead = () => {
|
|
1406
|
+
const { language } = useModernI18n();
|
|
1407
|
+
const location = useLocation();
|
|
1408
|
+
const currentLanguage = isSupportedLanguage(language) ? language : fallbackLanguage;
|
|
1409
|
+
const canonicalPath = localizedPath(location.pathname, currentLanguage);
|
|
1410
|
+
|
|
1411
|
+
return (
|
|
1412
|
+
<Helmet>
|
|
1413
|
+
<link rel="canonical" href={absoluteUrl(canonicalPath)} />
|
|
1414
|
+
{supportedLanguages.map((code) => (
|
|
1415
|
+
<link
|
|
1416
|
+
href={absoluteUrl(localizedPath(location.pathname, code))}
|
|
1417
|
+
hrefLang={code}
|
|
1418
|
+
key={code}
|
|
1419
|
+
rel="alternate"
|
|
1420
|
+
/>
|
|
1421
|
+
))}
|
|
1422
|
+
<link
|
|
1423
|
+
href={absoluteUrl(localizedPath(location.pathname, fallbackLanguage))}
|
|
1424
|
+
hrefLang="x-default"
|
|
1425
|
+
rel="alternate"
|
|
1426
|
+
/>
|
|
1427
|
+
</Helmet>
|
|
1428
|
+
);
|
|
1429
|
+
};
|
|
1430
|
+
`;
|
|
1431
|
+
}
|
|
1185
1432
|
function createShellPage() {
|
|
1186
|
-
return `
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1433
|
+
return `import { Helmet } from '@modern-js/runtime/head';
|
|
1434
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
1435
|
+
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
1436
|
+
import { useTranslation } from 'react-i18next';
|
|
1437
|
+
|
|
1438
|
+
const remotes = ['remote-commerce', 'remote-identity', 'remote-design-system'];
|
|
1191
1439
|
|
|
1440
|
+
${createLocalizedHeadComponent(true)}
|
|
1192
1441
|
export default function ShellHome() {
|
|
1442
|
+
const { t } = useTranslation();
|
|
1443
|
+
const { language } = useModernI18n();
|
|
1444
|
+
const location = useLocation();
|
|
1445
|
+
const currentLanguage = isSupportedLanguage(language) ? language : fallbackLanguage;
|
|
1446
|
+
const suffix = locationSuffix(location);
|
|
1447
|
+
const languageOptions = supportedLanguages.map((code) => ({
|
|
1448
|
+
code,
|
|
1449
|
+
href: \`\${localizedPath(location.pathname, code)}\${suffix}\`,
|
|
1450
|
+
label: t(\`language.\${code}\`),
|
|
1451
|
+
}));
|
|
1193
1452
|
return (
|
|
1194
1453
|
<main>
|
|
1195
|
-
<
|
|
1196
|
-
<
|
|
1454
|
+
<LocalizedHead />
|
|
1455
|
+
<nav aria-label={t('language.switcher')}>
|
|
1456
|
+
{languageOptions.map((option) => (
|
|
1457
|
+
<a
|
|
1458
|
+
aria-current={currentLanguage === option.code ? 'page' : undefined}
|
|
1459
|
+
href={option.href}
|
|
1460
|
+
key={option.code}
|
|
1461
|
+
>
|
|
1462
|
+
{option.label}
|
|
1463
|
+
</a>
|
|
1464
|
+
))}
|
|
1465
|
+
</nav>
|
|
1466
|
+
<h1>{t('shell.title')}</h1>
|
|
1467
|
+
<p data-testid="ultramodern-preset">{t('shell.preset')}</p>
|
|
1197
1468
|
<ul>
|
|
1198
|
-
{remotes.map(remote => (
|
|
1199
|
-
<li key={remote}>{remote}</li>
|
|
1469
|
+
{remotes.map((remote) => (
|
|
1470
|
+
<li key={remote}>{t(\`shell.remotes.\${remote}\`)}</li>
|
|
1200
1471
|
))}
|
|
1201
1472
|
</ul>
|
|
1202
1473
|
</main>
|
|
@@ -1205,11 +1476,20 @@ export default function ShellHome() {
|
|
|
1205
1476
|
`;
|
|
1206
1477
|
}
|
|
1207
1478
|
function createRemotePage(app) {
|
|
1208
|
-
return `
|
|
1479
|
+
return `import { Helmet } from '@modern-js/runtime/head';
|
|
1480
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
1481
|
+
import { useLocation } from '@modern-js/plugin-tanstack/runtime';
|
|
1482
|
+
import { useTranslation } from 'react-i18next';
|
|
1483
|
+
|
|
1484
|
+
${createLocalizedHeadComponent()}
|
|
1485
|
+
export default function ${toPascalCase(app.id)}Home() {
|
|
1486
|
+
const { t } = useTranslation();
|
|
1487
|
+
|
|
1209
1488
|
return (
|
|
1210
1489
|
<main>
|
|
1211
|
-
<
|
|
1212
|
-
<
|
|
1490
|
+
<LocalizedHead />
|
|
1491
|
+
<h1>{t('remote.title')}</h1>
|
|
1492
|
+
<p data-mf-role="${app.kind}">{t('remote.domain')}</p>
|
|
1213
1493
|
</main>
|
|
1214
1494
|
);
|
|
1215
1495
|
}
|
|
@@ -1224,17 +1504,21 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|
|
1224
1504
|
`;
|
|
1225
1505
|
}
|
|
1226
1506
|
function createRemoteEntry(app) {
|
|
1227
|
-
const
|
|
1228
|
-
return `export { default } from './components/${
|
|
1507
|
+
const componentFile = 'remote-identity' === app.id ? 'identity-widget' : 'commerce-widget';
|
|
1508
|
+
return `export { default } from './components/${componentFile}';
|
|
1229
1509
|
`;
|
|
1230
1510
|
}
|
|
1231
1511
|
function createRemoteWidget(app) {
|
|
1232
1512
|
const componentName = 'remote-identity' === app.id ? 'IdentityWidget' : 'CommerceWidget';
|
|
1233
|
-
return `
|
|
1513
|
+
return `import { useTranslation } from 'react-i18next';
|
|
1514
|
+
|
|
1515
|
+
export default function ${componentName}() {
|
|
1516
|
+
const { t } = useTranslation();
|
|
1517
|
+
|
|
1234
1518
|
return (
|
|
1235
1519
|
<section data-mf-remote="${app.id}">
|
|
1236
|
-
<h2
|
|
1237
|
-
<p>
|
|
1520
|
+
<h2>{t('remote.widget.title')}</h2>
|
|
1521
|
+
<p>{t('remote.widget.body')}</p>
|
|
1238
1522
|
</section>
|
|
1239
1523
|
);
|
|
1240
1524
|
}
|
|
@@ -1243,7 +1527,7 @@ function createRemoteWidget(app) {
|
|
|
1243
1527
|
function createDesignButton() {
|
|
1244
1528
|
return `import { designTokens } from '../tokens';
|
|
1245
1529
|
|
|
1246
|
-
export default function Button({ label
|
|
1530
|
+
export default function Button({ label }: { label: string }) {
|
|
1247
1531
|
return (
|
|
1248
1532
|
<button
|
|
1249
1533
|
type="button"
|
|
@@ -1261,8 +1545,8 @@ export default function Button({ label = 'Design System Button' }: { label?: str
|
|
|
1261
1545
|
function createDesignTokens() {
|
|
1262
1546
|
return `export const designTokens = {
|
|
1263
1547
|
color: {
|
|
1264
|
-
foreground: '#133225',
|
|
1265
1548
|
accent: '#2f8f68',
|
|
1549
|
+
foreground: '#133225',
|
|
1266
1550
|
},
|
|
1267
1551
|
radius: {
|
|
1268
1552
|
control: '999px',
|
|
@@ -1270,6 +1554,62 @@ function createDesignTokens() {
|
|
|
1270
1554
|
} as const;
|
|
1271
1555
|
`;
|
|
1272
1556
|
}
|
|
1557
|
+
function createEnglishTranslations(app) {
|
|
1558
|
+
if ('shell' === app.kind) return {
|
|
1559
|
+
language: {
|
|
1560
|
+
cs: 'Czech',
|
|
1561
|
+
en: 'English',
|
|
1562
|
+
switcher: 'Language'
|
|
1563
|
+
},
|
|
1564
|
+
shell: {
|
|
1565
|
+
preset: 'presetUltramodern workspace',
|
|
1566
|
+
remotes: {
|
|
1567
|
+
'remote-commerce': 'Commerce Remote',
|
|
1568
|
+
'remote-design-system': 'Design System Remote',
|
|
1569
|
+
'remote-identity': 'Identity Remote'
|
|
1570
|
+
},
|
|
1571
|
+
title: 'UltraModern SuperApp Shell'
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
return {
|
|
1575
|
+
remote: {
|
|
1576
|
+
domain: app.domain ?? app.kind,
|
|
1577
|
+
title: app.displayName,
|
|
1578
|
+
widget: {
|
|
1579
|
+
body: 'vertical' === app.kind ? `Owns the ${app.domain} vertical route surface.` : 'Provides shared UI primitives for the workspace.',
|
|
1580
|
+
title: app.displayName
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
function createCzechTranslations(app) {
|
|
1586
|
+
if ('shell' === app.kind) return {
|
|
1587
|
+
language: {
|
|
1588
|
+
cs: 'Cestina',
|
|
1589
|
+
en: 'Anglictina',
|
|
1590
|
+
switcher: 'Jazyk'
|
|
1591
|
+
},
|
|
1592
|
+
shell: {
|
|
1593
|
+
preset: 'presetUltramodern workspace',
|
|
1594
|
+
remotes: {
|
|
1595
|
+
'remote-commerce': 'Commerce remote',
|
|
1596
|
+
'remote-design-system': 'Design system remote',
|
|
1597
|
+
'remote-identity': 'Identity remote'
|
|
1598
|
+
},
|
|
1599
|
+
title: 'UltraModern SuperApp shell'
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
return {
|
|
1603
|
+
remote: {
|
|
1604
|
+
domain: app.domain ?? app.kind,
|
|
1605
|
+
title: app.displayName,
|
|
1606
|
+
widget: {
|
|
1607
|
+
body: 'vertical' === app.kind ? `Vlastni ${app.domain} vertical route surface.` : 'Poskytuje sdilene UI prvky pro workspace.',
|
|
1608
|
+
title: app.displayName
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1273
1613
|
function createEffectSharedApi() {
|
|
1274
1614
|
return `import {
|
|
1275
1615
|
HttpApi,
|
|
@@ -1283,9 +1623,7 @@ const recommendationSchema = Schema.Struct({
|
|
|
1283
1623
|
title: Schema.String,
|
|
1284
1624
|
});
|
|
1285
1625
|
|
|
1286
|
-
export const recommendationsEffectApi = HttpApi.make(
|
|
1287
|
-
'RecommendationsEffectApi',
|
|
1288
|
-
).add(
|
|
1626
|
+
export const recommendationsEffectApi = HttpApi.make('RecommendationsEffectApi').add(
|
|
1289
1627
|
HttpApiGroup.make('recommendations').add(
|
|
1290
1628
|
HttpApiEndpoint.get('list', '/effect/recommendations', {
|
|
1291
1629
|
success: Schema.Struct({
|
|
@@ -1308,7 +1646,7 @@ import { recommendationsEffectApi } from '../../shared/effect/api';
|
|
|
1308
1646
|
const recommendationsLayer = HttpApiBuilder.group(
|
|
1309
1647
|
recommendationsEffectApi,
|
|
1310
1648
|
'recommendations',
|
|
1311
|
-
(handlers
|
|
1649
|
+
(handlers) =>
|
|
1312
1650
|
handlers.handle('list', () =>
|
|
1313
1651
|
Effect.succeed({
|
|
1314
1652
|
items: [
|
|
@@ -1532,11 +1870,17 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
1532
1870
|
materialization: {
|
|
1533
1871
|
targetRoot: 'generated-project-root',
|
|
1534
1872
|
allowedPaths: [
|
|
1873
|
+
'.agents/**',
|
|
1874
|
+
'.github/**',
|
|
1875
|
+
'.gitignore',
|
|
1535
1876
|
'.modernjs/**',
|
|
1877
|
+
'AGENTS.md',
|
|
1536
1878
|
'README.md',
|
|
1537
1879
|
'apps/**',
|
|
1538
1880
|
'packages/**',
|
|
1539
1881
|
'package.json',
|
|
1882
|
+
'oxfmt.config.ts',
|
|
1883
|
+
'oxlint.config.ts',
|
|
1540
1884
|
'pnpm-workspace.yaml',
|
|
1541
1885
|
"scripts/**",
|
|
1542
1886
|
'services/**',
|
|
@@ -1545,7 +1889,6 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
1545
1889
|
],
|
|
1546
1890
|
deniedPaths: [
|
|
1547
1891
|
'.git/**',
|
|
1548
|
-
'.github/**',
|
|
1549
1892
|
'.npmrc',
|
|
1550
1893
|
'.yarnrc',
|
|
1551
1894
|
'.env',
|
|
@@ -1561,6 +1904,22 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
1561
1904
|
modernPackageSpecifier: modernPackageVersion(packageSource),
|
|
1562
1905
|
generatedWorkspacePackageSpecifier: WORKSPACE_PACKAGE_VERSION
|
|
1563
1906
|
},
|
|
1907
|
+
agentSkills: {
|
|
1908
|
+
installDir: '.agents/skills',
|
|
1909
|
+
source: {
|
|
1910
|
+
repository: 'https://github.com/rstackjs/agent-skills',
|
|
1911
|
+
commit: RSTACK_AGENT_SKILLS_COMMIT,
|
|
1912
|
+
license: 'MIT',
|
|
1913
|
+
licensePath: '.agents/rstackjs-agent-skills-LICENSE'
|
|
1914
|
+
},
|
|
1915
|
+
baseline: baselineAgentSkills,
|
|
1916
|
+
privateSource: {
|
|
1917
|
+
repository: 'https://github.com/TechsioCZ/skills',
|
|
1918
|
+
install: 'clone-if-authorized',
|
|
1919
|
+
baseline: privateAgentSkills
|
|
1920
|
+
},
|
|
1921
|
+
lockFile: '.agents/skills-lock.json'
|
|
1922
|
+
},
|
|
1564
1923
|
validation: {
|
|
1565
1924
|
schemaValidation: true,
|
|
1566
1925
|
sourceValidation: [
|
|
@@ -1577,10 +1936,12 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
1577
1936
|
],
|
|
1578
1937
|
postMaterializationValidation: [
|
|
1579
1938
|
'ultramodern-workspace-contract-check',
|
|
1939
|
+
'github-workflow-security-enforced',
|
|
1940
|
+
'pnpm-11-policy-enforced',
|
|
1580
1941
|
'template-manifest-retained'
|
|
1581
1942
|
],
|
|
1582
1943
|
expectedCommands: [
|
|
1583
|
-
|
|
1944
|
+
'pnpm install',
|
|
1584
1945
|
'pnpm run ultramodern:check'
|
|
1585
1946
|
]
|
|
1586
1947
|
}
|
|
@@ -1589,18 +1950,21 @@ function createTemplateManifest(modernVersion, packageSource) {
|
|
|
1589
1950
|
function writeApp(targetDir, scope, app, packageSource) {
|
|
1590
1951
|
writeJson(targetDir, `${app.directory}/package.json`, createAppPackage(scope, app, packageSource));
|
|
1591
1952
|
writeJson(targetDir, `${app.directory}/tsconfig.json`, createPackageTsConfig(app.directory));
|
|
1592
|
-
writeFile(targetDir, `${app.directory}/src/modern-app-env.d.ts`, "/// <reference types='@modern-js/app-tools/types' />\n");
|
|
1953
|
+
writeFile(targetDir, `${app.directory}/src/modern-app-env.d.ts`, "/// <reference types='@modern-js/app-tools/types' />\n\ndeclare const ULTRAMODERN_SITE_URL: string;\n");
|
|
1593
1954
|
writeFile(targetDir, `${app.directory}/modern.config.ts`, createAppModernConfig(app));
|
|
1955
|
+
writeFile(targetDir, `${app.directory}/src/modern.runtime.ts`, createAppRuntimeConfig());
|
|
1956
|
+
writeJson(targetDir, `${app.directory}/config/public/locales/en/translation.json`, createEnglishTranslations(app));
|
|
1957
|
+
writeJson(targetDir, `${app.directory}/config/public/locales/cs/translation.json`, createCzechTranslations(app));
|
|
1594
1958
|
writeFile(targetDir, `${app.directory}/module-federation.config.ts`, 'shell' === app.kind ? createShellModuleFederationConfig() : createRemoteModuleFederationConfig(app));
|
|
1595
1959
|
writeFile(targetDir, `${app.directory}/src/routes/layout.tsx`, createLayout(app.id));
|
|
1596
|
-
writeFile(targetDir, `${app.directory}/src/routes/page.tsx`, 'shell' === app.kind ? createShellPage() : createRemotePage(app));
|
|
1960
|
+
writeFile(targetDir, `${app.directory}/src/routes/[lang]/page.tsx`, 'shell' === app.kind ? createShellPage() : createRemotePage(app));
|
|
1597
1961
|
if ('vertical' === app.kind) {
|
|
1598
1962
|
writeFile(targetDir, `${app.directory}/src/remote-entry.tsx`, createRemoteEntry(app));
|
|
1599
|
-
const widgetFile = 'remote-identity' === app.id ? '
|
|
1963
|
+
const widgetFile = 'remote-identity' === app.id ? 'identity-widget.tsx' : 'commerce-widget.tsx';
|
|
1600
1964
|
writeFile(targetDir, `${app.directory}/src/components/${widgetFile}`, createRemoteWidget(app));
|
|
1601
1965
|
}
|
|
1602
1966
|
if ('horizontal-design-system' === app.kind) {
|
|
1603
|
-
writeFile(targetDir, `${app.directory}/src/components/
|
|
1967
|
+
writeFile(targetDir, `${app.directory}/src/components/button.tsx`, createDesignButton());
|
|
1604
1968
|
writeFile(targetDir, `${app.directory}/src/tokens.ts`, createDesignTokens());
|
|
1605
1969
|
}
|
|
1606
1970
|
}
|
|
@@ -1627,27 +1991,27 @@ function writeSharedPackages(targetDir, scope) {
|
|
|
1627
1991
|
});
|
|
1628
1992
|
}
|
|
1629
1993
|
writeFile(targetDir, 'packages/shared-contracts/src/index.ts', `export const ultramodernWorkspaceContract = {
|
|
1994
|
+
ownership: 'topology/ownership.json',
|
|
1630
1995
|
preset: 'presetUltramodern',
|
|
1631
1996
|
topology: 'topology/reference-topology.json',
|
|
1632
|
-
ownership: 'topology/ownership.json',
|
|
1633
1997
|
} as const;
|
|
1634
1998
|
`);
|
|
1635
1999
|
writeFile(targetDir, 'packages/shared-design-tokens/src/index.ts', `export const sharedDesignTokens = {
|
|
1636
2000
|
color: {
|
|
1637
|
-
surface: '#f6fbf7',
|
|
1638
|
-
foreground: '#133225',
|
|
1639
2001
|
accent: '#2f8f68',
|
|
2002
|
+
foreground: '#133225',
|
|
2003
|
+
surface: '#f6fbf7',
|
|
1640
2004
|
},
|
|
1641
2005
|
} as const;
|
|
1642
2006
|
`);
|
|
1643
|
-
writeFile(targetDir, 'packages/shared-effect-api/src/index.ts', `export
|
|
2007
|
+
writeFile(targetDir, 'packages/shared-effect-api/src/index.ts', `export interface Recommendation {
|
|
1644
2008
|
id: string;
|
|
1645
2009
|
title: string;
|
|
1646
|
-
}
|
|
2010
|
+
}
|
|
1647
2011
|
|
|
1648
2012
|
export const recommendationsApiContract = {
|
|
1649
|
-
serviceId: '${effectService.id}',
|
|
1650
2013
|
basePath: '/recommendations-api/effect/recommendations',
|
|
2014
|
+
serviceId: '${effectService.id}',
|
|
1651
2015
|
} as const;
|
|
1652
2016
|
`);
|
|
1653
2017
|
}
|
|
@@ -1662,7 +2026,7 @@ function generateUltramodernWorkspace(options) {
|
|
|
1662
2026
|
packageScope: scope
|
|
1663
2027
|
});
|
|
1664
2028
|
writeJson(options.targetDir, 'package.json', createRootPackageJson(scope, packageSource));
|
|
1665
|
-
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase(
|
|
2029
|
+
writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase());
|
|
1666
2030
|
writeJson(options.targetDir, 'topology/reference-topology.json', createTopology(scope));
|
|
1667
2031
|
writeJson(options.targetDir, 'topology/ownership.json', createOwnership(scope));
|
|
1668
2032
|
writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay());
|
|
@@ -1683,7 +2047,6 @@ const templateIdPattern = /^[a-z0-9][a-z0-9._-]*$/;
|
|
|
1683
2047
|
const packageNamePattern = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
|
|
1684
2048
|
const requiredDeniedPaths = [
|
|
1685
2049
|
'.git/**',
|
|
1686
|
-
'.github/**',
|
|
1687
2050
|
'.npmrc',
|
|
1688
2051
|
'.yarnrc',
|
|
1689
2052
|
'.env',
|
|
@@ -1746,7 +2109,7 @@ function detectBffRuntime() {
|
|
|
1746
2109
|
process.exit(1);
|
|
1747
2110
|
}
|
|
1748
2111
|
function src_renderTemplate(template, data) {
|
|
1749
|
-
const tagRegex = /\{\{(#if|#unless|\/if|\/unless)(?:\s+(\w+))
|
|
2112
|
+
const tagRegex = /\{\{(~?)(#if|#unless|\/if|\/unless)(?:\s+(\w+))?(~?)\}\}/g;
|
|
1750
2113
|
function renderConditionals(startIndex, expectedClose) {
|
|
1751
2114
|
let rendered = '';
|
|
1752
2115
|
let cursor = startIndex;
|
|
@@ -1757,7 +2120,7 @@ function src_renderTemplate(template, data) {
|
|
|
1757
2120
|
rendered: rendered + template.slice(cursor),
|
|
1758
2121
|
nextIndex: template.length
|
|
1759
2122
|
};
|
|
1760
|
-
const [raw, tag, condition] = match;
|
|
2123
|
+
const [raw, , tag, condition, rightTrim] = match;
|
|
1761
2124
|
const tagIndex = match.index;
|
|
1762
2125
|
rendered += template.slice(cursor, tagIndex);
|
|
1763
2126
|
cursor = tagIndex + raw.length;
|
|
@@ -1773,10 +2136,17 @@ function src_renderTemplate(template, data) {
|
|
|
1773
2136
|
}
|
|
1774
2137
|
if ('/if' === tag || '/unless' === tag) {
|
|
1775
2138
|
const kind = '/if' === tag ? 'if' : 'unless';
|
|
1776
|
-
if (expectedClose === kind)
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2139
|
+
if (expectedClose === kind) {
|
|
2140
|
+
let nextIndex = cursor;
|
|
2141
|
+
if ('~' === rightTrim) {
|
|
2142
|
+
const trailingWhitespace = /^\s*/u.exec(template.slice(nextIndex));
|
|
2143
|
+
nextIndex += trailingWhitespace?.[0].length ?? 0;
|
|
2144
|
+
}
|
|
2145
|
+
return {
|
|
2146
|
+
rendered,
|
|
2147
|
+
nextIndex
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
1780
2150
|
rendered += raw;
|
|
1781
2151
|
}
|
|
1782
2152
|
}
|
|
@@ -1857,20 +2227,28 @@ function createBuiltinTemplateManifest(version) {
|
|
|
1857
2227
|
materialization: {
|
|
1858
2228
|
targetRoot: 'generated-project-root',
|
|
1859
2229
|
allowedPaths: [
|
|
2230
|
+
'.agents/**',
|
|
1860
2231
|
'.browserslistrc',
|
|
2232
|
+
'.github/**',
|
|
1861
2233
|
'.gitignore',
|
|
1862
2234
|
'.modernjs/**',
|
|
1863
2235
|
'.nvmrc',
|
|
2236
|
+
'AGENTS.md',
|
|
1864
2237
|
'README.md',
|
|
1865
2238
|
'api/**',
|
|
1866
|
-
'
|
|
2239
|
+
'config/**',
|
|
1867
2240
|
'modern.config.ts',
|
|
2241
|
+
'oxfmt.config.ts',
|
|
2242
|
+
'oxlint.config.ts',
|
|
1868
2243
|
'package.json',
|
|
2244
|
+
'pnpm-workspace.yaml',
|
|
1869
2245
|
'postcss.config.mjs',
|
|
2246
|
+
'rstest.config.mts',
|
|
1870
2247
|
"scripts/**",
|
|
1871
2248
|
'shared/**',
|
|
1872
2249
|
'src/**',
|
|
1873
2250
|
'tailwind.config.ts',
|
|
2251
|
+
'tests/**',
|
|
1874
2252
|
'tsconfig.json'
|
|
1875
2253
|
],
|
|
1876
2254
|
deniedPaths: requiredDeniedPaths,
|
|
@@ -1899,10 +2277,15 @@ function createBuiltinTemplateManifest(version) {
|
|
|
1899
2277
|
postMaterializationValidation: [
|
|
1900
2278
|
'ultramodern-contract-check',
|
|
1901
2279
|
'dependency-install-with-lifecycle-deny',
|
|
2280
|
+
'github-workflow-security-enforced',
|
|
2281
|
+
'package-source-retained',
|
|
2282
|
+
'pnpm-11-policy-enforced',
|
|
2283
|
+
'rstest-smoke-tests',
|
|
1902
2284
|
'template-manifest-retained'
|
|
1903
2285
|
],
|
|
1904
2286
|
expectedCommands: [
|
|
1905
|
-
|
|
2287
|
+
'pnpm install',
|
|
2288
|
+
'pnpm test',
|
|
1906
2289
|
'pnpm run ultramodern:check'
|
|
1907
2290
|
]
|
|
1908
2291
|
}
|
|
@@ -2008,9 +2391,19 @@ function writeTemplateManifestEvidence(targetDir, manifest) {
|
|
|
2008
2391
|
});
|
|
2009
2392
|
node_fs.writeFileSync(evidencePath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
2010
2393
|
}
|
|
2011
|
-
function
|
|
2394
|
+
function readCreatePackageJson() {
|
|
2012
2395
|
const createPackageJson = node_path.resolve(src_dirname, '..', 'package.json');
|
|
2013
|
-
|
|
2396
|
+
return JSON.parse(node_fs.readFileSync(createPackageJson, 'utf-8'));
|
|
2397
|
+
}
|
|
2398
|
+
function isBleedingDevCreatePackage(createPackage) {
|
|
2399
|
+
return '@bleedingdev/modern-js-create' === createPackage.name;
|
|
2400
|
+
}
|
|
2401
|
+
function getBleedingDevFrameworkVersion(createPackage, fallbackVersion) {
|
|
2402
|
+
const frameworkVersion = createPackage.ultramodern?.frameworkVersion;
|
|
2403
|
+
return 'string' == typeof frameworkVersion && frameworkVersion.length > 0 ? frameworkVersion : fallbackVersion;
|
|
2404
|
+
}
|
|
2405
|
+
function showVersion() {
|
|
2406
|
+
const createPackage = readCreatePackageJson();
|
|
2014
2407
|
const version = createPackage.version || 'unknown';
|
|
2015
2408
|
console.log(i18n.t(localeKeys.version.message, {
|
|
2016
2409
|
version
|
|
@@ -2081,14 +2474,15 @@ function detectWorkspaceProtocolFlag() {
|
|
|
2081
2474
|
const args = process.argv.slice(2);
|
|
2082
2475
|
return args.includes('--workspace');
|
|
2083
2476
|
}
|
|
2084
|
-
function detectUltramodernWorkspaceFlag() {
|
|
2477
|
+
function detectUltramodernWorkspaceFlag(createPackage) {
|
|
2085
2478
|
const args = process.argv.slice(2);
|
|
2086
|
-
return args.includes(ULTRAMODERN_WORKSPACE_FLAG);
|
|
2479
|
+
return args.includes(ULTRAMODERN_WORKSPACE_FLAG) || isBleedingDevCreatePackage(createPackage);
|
|
2087
2480
|
}
|
|
2088
|
-
function detectUltramodernPackageSource(args,
|
|
2481
|
+
function detectUltramodernPackageSource(args, defaultPackageVersion, createPackage) {
|
|
2482
|
+
const bleedingDevDefaults = isBleedingDevCreatePackage(createPackage);
|
|
2089
2483
|
const strategy = getOptionValue(args, [
|
|
2090
2484
|
'--ultramodern-package-source'
|
|
2091
|
-
]) ?? 'workspace';
|
|
2485
|
+
]) ?? (bleedingDevDefaults ? 'install' : 'workspace');
|
|
2092
2486
|
if ('workspace' !== strategy && 'install' !== strategy) {
|
|
2093
2487
|
console.error('--ultramodern-package-source must be "workspace" or "install"');
|
|
2094
2488
|
process.exit(1);
|
|
@@ -2097,18 +2491,68 @@ function detectUltramodernPackageSource(args, modernVersion) {
|
|
|
2097
2491
|
strategy,
|
|
2098
2492
|
modernPackageVersion: getOptionValue(args, [
|
|
2099
2493
|
'--ultramodern-package-version'
|
|
2100
|
-
]) ??
|
|
2494
|
+
]) ?? defaultPackageVersion,
|
|
2101
2495
|
registry: getOptionValue(args, [
|
|
2102
2496
|
'--ultramodern-package-registry'
|
|
2103
2497
|
]),
|
|
2104
2498
|
aliasScope: getOptionValue(args, [
|
|
2105
2499
|
'--ultramodern-package-scope'
|
|
2106
|
-
]),
|
|
2500
|
+
]) ?? (bleedingDevDefaults && 'install' === strategy ? 'bleedingdev' : void 0),
|
|
2107
2501
|
aliasPackageNamePrefix: getOptionValue(args, [
|
|
2108
2502
|
'--ultramodern-package-name-prefix'
|
|
2109
2503
|
]) ?? 'modern-js-'
|
|
2110
2504
|
};
|
|
2111
2505
|
}
|
|
2506
|
+
function src_modernAliasPackageName(packageName, packageSource) {
|
|
2507
|
+
if (!packageSource.aliasScope) return packageName;
|
|
2508
|
+
const scope = packageSource.aliasScope.replace(/^@/, '');
|
|
2509
|
+
const unscopedName = packageName.split('/').at(-1);
|
|
2510
|
+
return `@${scope}/${packageSource.aliasPackageNamePrefix ?? ''}${unscopedName}`;
|
|
2511
|
+
}
|
|
2512
|
+
function singleAppModernPackageSpecifier(packageName, packageSource, useWorkspaceProtocol) {
|
|
2513
|
+
if (useWorkspaceProtocol) return 'workspace:*';
|
|
2514
|
+
if ('install' !== packageSource.strategy || !packageSource.aliasScope) return packageSource.modernPackageVersion;
|
|
2515
|
+
return `npm:${src_modernAliasPackageName(packageName, packageSource)}@${packageSource.modernPackageVersion}`;
|
|
2516
|
+
}
|
|
2517
|
+
const singleAppModernPackages = [
|
|
2518
|
+
'@modern-js/runtime',
|
|
2519
|
+
'@modern-js/app-tools',
|
|
2520
|
+
'@modern-js/tsconfig',
|
|
2521
|
+
'@modern-js/plugin-i18n',
|
|
2522
|
+
'@modern-js/plugin-tanstack',
|
|
2523
|
+
'@modern-js/plugin-bff',
|
|
2524
|
+
'@modern-js/adapter-rstest'
|
|
2525
|
+
];
|
|
2526
|
+
function createSingleAppPackageSourceEvidence(packageSource, useWorkspaceProtocol) {
|
|
2527
|
+
const strategy = useWorkspaceProtocol ? 'workspace' : 'install';
|
|
2528
|
+
const specifier = useWorkspaceProtocol ? 'workspace:*' : packageSource.modernPackageVersion;
|
|
2529
|
+
const aliases = 'install' === strategy && packageSource.aliasScope ? Object.fromEntries(singleAppModernPackages.map((packageName)=>[
|
|
2530
|
+
packageName,
|
|
2531
|
+
src_modernAliasPackageName(packageName, packageSource)
|
|
2532
|
+
])) : void 0;
|
|
2533
|
+
return {
|
|
2534
|
+
schemaVersion: 1,
|
|
2535
|
+
preset: 'presetUltramodern',
|
|
2536
|
+
strategy,
|
|
2537
|
+
modernPackages: {
|
|
2538
|
+
specifier,
|
|
2539
|
+
packages: singleAppModernPackages,
|
|
2540
|
+
...packageSource.registry ? {
|
|
2541
|
+
registry: packageSource.registry
|
|
2542
|
+
} : {},
|
|
2543
|
+
...aliases ? {
|
|
2544
|
+
aliases
|
|
2545
|
+
} : {}
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
function writeSingleAppPackageSourceEvidence(targetDir, packageSource, useWorkspaceProtocol) {
|
|
2550
|
+
const evidencePath = node_path.join(targetDir, '.modernjs', 'ultramodern-package-source.json');
|
|
2551
|
+
node_fs.mkdirSync(node_path.dirname(evidencePath), {
|
|
2552
|
+
recursive: true
|
|
2553
|
+
});
|
|
2554
|
+
node_fs.writeFileSync(evidencePath, `${JSON.stringify(createSingleAppPackageSourceEvidence(packageSource, useWorkspaceProtocol), null, 2)}\n`);
|
|
2555
|
+
}
|
|
2112
2556
|
function isDirectoryEmpty(dirPath) {
|
|
2113
2557
|
if (!node_fs.existsSync(dirPath)) return false;
|
|
2114
2558
|
try {
|
|
@@ -2194,16 +2638,16 @@ async function main() {
|
|
|
2194
2638
|
process.exit(1);
|
|
2195
2639
|
}
|
|
2196
2640
|
}
|
|
2197
|
-
const
|
|
2198
|
-
const createPackage = JSON.parse(node_fs.readFileSync(createPackageJson, 'utf-8'));
|
|
2641
|
+
const createPackage = readCreatePackageJson();
|
|
2199
2642
|
const version = createPackage.version || 'latest';
|
|
2200
|
-
const
|
|
2643
|
+
const ultramodernPackageVersion = isBleedingDevCreatePackage(createPackage) ? getBleedingDevFrameworkVersion(createPackage, version) : version;
|
|
2644
|
+
const generateWorkspace = detectUltramodernWorkspaceFlag(createPackage);
|
|
2201
2645
|
if (generateWorkspace) {
|
|
2202
2646
|
generateUltramodernWorkspace({
|
|
2203
2647
|
targetDir,
|
|
2204
2648
|
packageName: generatedPackageName,
|
|
2205
2649
|
modernVersion: version,
|
|
2206
|
-
packageSource: detectUltramodernPackageSource(args,
|
|
2650
|
+
packageSource: detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage)
|
|
2207
2651
|
});
|
|
2208
2652
|
const dim = '\x1b[2m\x1b[3m';
|
|
2209
2653
|
const reset = '\x1b[0m';
|
|
@@ -2223,12 +2667,19 @@ async function main() {
|
|
|
2223
2667
|
const bffRuntime = detectBffRuntime();
|
|
2224
2668
|
const enableTailwind = detectTailwindFlag();
|
|
2225
2669
|
const useWorkspaceProtocol = detectWorkspaceProtocolFlag();
|
|
2226
|
-
const
|
|
2670
|
+
const packageSource = detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage);
|
|
2227
2671
|
const templateManifest = createBuiltinTemplateManifest(version);
|
|
2228
2672
|
validateTemplateManifest(templateManifest);
|
|
2229
2673
|
copyTemplate(templateDir, targetDir, {
|
|
2230
2674
|
packageName: generatedPackageName,
|
|
2231
|
-
version:
|
|
2675
|
+
version: useWorkspaceProtocol ? 'workspace:*' : packageSource.modernPackageVersion,
|
|
2676
|
+
runtimeVersion: singleAppModernPackageSpecifier('@modern-js/runtime', packageSource, useWorkspaceProtocol),
|
|
2677
|
+
appToolsVersion: singleAppModernPackageSpecifier('@modern-js/app-tools', packageSource, useWorkspaceProtocol),
|
|
2678
|
+
adapterRstestVersion: singleAppModernPackageSpecifier('@modern-js/adapter-rstest', packageSource, useWorkspaceProtocol),
|
|
2679
|
+
tsconfigVersion: singleAppModernPackageSpecifier('@modern-js/tsconfig', packageSource, useWorkspaceProtocol),
|
|
2680
|
+
pluginTanstackVersion: singleAppModernPackageSpecifier('@modern-js/plugin-tanstack', packageSource, useWorkspaceProtocol),
|
|
2681
|
+
pluginBffVersion: singleAppModernPackageSpecifier('@modern-js/plugin-bff', packageSource, useWorkspaceProtocol),
|
|
2682
|
+
pluginI18nVersion: singleAppModernPackageSpecifier('@modern-js/plugin-i18n', packageSource, useWorkspaceProtocol),
|
|
2232
2683
|
isSubproject,
|
|
2233
2684
|
routerFramework,
|
|
2234
2685
|
bffRuntime,
|
|
@@ -2238,21 +2689,37 @@ async function main() {
|
|
|
2238
2689
|
const targetPackageJson = node_path.join(targetDir, 'package.json');
|
|
2239
2690
|
const packageJson = JSON.parse(node_fs.readFileSync(targetPackageJson, 'utf-8'));
|
|
2240
2691
|
packageJson.name = generatedPackageName;
|
|
2692
|
+
packageJson.modernjs = {
|
|
2693
|
+
...packageJson.modernjs ?? {},
|
|
2694
|
+
preset: 'presetUltramodern',
|
|
2695
|
+
packageSource: {
|
|
2696
|
+
strategy: useWorkspaceProtocol ? 'workspace' : 'install',
|
|
2697
|
+
config: './.modernjs/ultramodern-package-source.json'
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2241
2700
|
if (isSubproject) {
|
|
2242
2701
|
delete packageJson['lint-staged'];
|
|
2243
2702
|
delete packageJson['simple-git-hooks'];
|
|
2244
2703
|
if (packageJson.scripts) {
|
|
2245
2704
|
delete packageJson.scripts.prepare;
|
|
2705
|
+
delete packageJson.scripts.format;
|
|
2706
|
+
delete packageJson.scripts['format:check'];
|
|
2246
2707
|
delete packageJson.scripts.lint;
|
|
2708
|
+
delete packageJson.scripts['lint:fix'];
|
|
2709
|
+
delete packageJson.scripts['skills:install'];
|
|
2710
|
+
delete packageJson.scripts['skills:check'];
|
|
2247
2711
|
}
|
|
2248
2712
|
if (packageJson.devDependencies) {
|
|
2249
2713
|
delete packageJson.devDependencies['lint-staged'];
|
|
2250
2714
|
delete packageJson.devDependencies['simple-git-hooks'];
|
|
2251
|
-
delete packageJson.devDependencies
|
|
2715
|
+
delete packageJson.devDependencies.oxlint;
|
|
2716
|
+
delete packageJson.devDependencies.oxfmt;
|
|
2717
|
+
delete packageJson.devDependencies.ultracite;
|
|
2252
2718
|
}
|
|
2253
2719
|
}
|
|
2254
2720
|
node_fs.writeFileSync(targetPackageJson, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
2255
2721
|
writeTemplateManifestEvidence(targetDir, templateManifest);
|
|
2722
|
+
writeSingleAppPackageSourceEvidence(targetDir, packageSource, useWorkspaceProtocol);
|
|
2256
2723
|
const dim = '\x1b[2m\x1b[3m';
|
|
2257
2724
|
const reset = '\x1b[0m';
|
|
2258
2725
|
console.log(`${i18n.t(localeKeys.message.success)}\n`);
|
|
@@ -2268,10 +2735,14 @@ function copyTemplate(src, dest, options) {
|
|
|
2268
2735
|
recursive: true
|
|
2269
2736
|
});
|
|
2270
2737
|
const excludeInSubproject = [
|
|
2738
|
+
'.agents',
|
|
2739
|
+
'.github',
|
|
2271
2740
|
'.gitignore.handlebars',
|
|
2272
|
-
'
|
|
2741
|
+
'AGENTS.md',
|
|
2273
2742
|
'.npmrc',
|
|
2274
|
-
'.nvmrc'
|
|
2743
|
+
'.nvmrc',
|
|
2744
|
+
'oxfmt.config.ts',
|
|
2745
|
+
'oxlint.config.ts'
|
|
2275
2746
|
];
|
|
2276
2747
|
function copyRecursive(srcDir, destDir) {
|
|
2277
2748
|
const entries = node_fs.readdirSync(srcDir, {
|
|
@@ -2293,6 +2764,13 @@ function copyTemplate(src, dest, options) {
|
|
|
2293
2764
|
const rendered = src_renderTemplate(templateContent, {
|
|
2294
2765
|
packageName: options.packageName,
|
|
2295
2766
|
version: options.version,
|
|
2767
|
+
runtimeVersion: options.runtimeVersion,
|
|
2768
|
+
appToolsVersion: options.appToolsVersion,
|
|
2769
|
+
adapterRstestVersion: options.adapterRstestVersion,
|
|
2770
|
+
tsconfigVersion: options.tsconfigVersion,
|
|
2771
|
+
pluginTanstackVersion: options.pluginTanstackVersion,
|
|
2772
|
+
pluginBffVersion: options.pluginBffVersion,
|
|
2773
|
+
pluginI18nVersion: options.pluginI18nVersion,
|
|
2296
2774
|
isSubproject: options.isSubproject,
|
|
2297
2775
|
isTanstackRouter: 'tanstack' === options.routerFramework,
|
|
2298
2776
|
enableBff: 'none' !== options.bffRuntime,
|
|
@@ -2300,7 +2778,7 @@ function copyTemplate(src, dest, options) {
|
|
|
2300
2778
|
useHonoBff: 'hono' === options.bffRuntime,
|
|
2301
2779
|
bffRuntime: options.bffRuntime,
|
|
2302
2780
|
enableTailwind: options.enableTailwind,
|
|
2303
|
-
|
|
2781
|
+
routerRuntimeImport: 'tanstack' === options.routerFramework ? '@modern-js/plugin-tanstack/runtime' : '@modern-js/runtime/router'
|
|
2304
2782
|
});
|
|
2305
2783
|
if (0 === rendered.trim().length) continue;
|
|
2306
2784
|
destPath = destPath.replace(/\.handlebars$/, '');
|