@bleedingdev/modern-js-create 3.5.0-ultramodern.2 → 3.5.0-ultramodern.20

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.
Files changed (32) hide show
  1. package/README.md +20 -14
  2. package/dist/cjs/ultramodern-tooling/commands.cjs +357 -3
  3. package/dist/cjs/ultramodern-workspace/app-files.cjs +8 -11
  4. package/dist/cjs/ultramodern-workspace/module-federation.cjs +13 -6
  5. package/dist/cjs/ultramodern-workspace/package-json.cjs +15 -8
  6. package/dist/cjs/ultramodern-workspace/versions.cjs +5 -0
  7. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +2 -0
  8. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +2 -0
  9. package/dist/esm/ultramodern-tooling/commands.js +357 -3
  10. package/dist/esm/ultramodern-workspace/app-files.js +8 -11
  11. package/dist/esm/ultramodern-workspace/module-federation.js +13 -6
  12. package/dist/esm/ultramodern-workspace/package-json.js +5 -8
  13. package/dist/esm/ultramodern-workspace/versions.js +3 -1
  14. package/dist/esm/ultramodern-workspace/workspace-scripts.js +3 -1
  15. package/dist/esm/ultramodern-workspace/write-workspace.js +3 -1
  16. package/dist/esm-node/ultramodern-tooling/commands.js +357 -3
  17. package/dist/esm-node/ultramodern-workspace/app-files.js +8 -11
  18. package/dist/esm-node/ultramodern-workspace/module-federation.js +13 -6
  19. package/dist/esm-node/ultramodern-workspace/package-json.js +5 -8
  20. package/dist/esm-node/ultramodern-workspace/versions.js +3 -1
  21. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +3 -1
  22. package/dist/esm-node/ultramodern-workspace/write-workspace.js +3 -1
  23. package/dist/types/ultramodern-workspace/versions.d.ts +2 -0
  24. package/package.json +3 -3
  25. package/template-workspace/.gitignore.handlebars +3 -0
  26. package/template-workspace/README.md.handlebars +19 -13
  27. package/template-workspace/patches/@module-federation__bridge-react@2.6.0.patch +140 -0
  28. package/template-workspace/patches/@module-federation__modern-js-v3@2.6.0.patch +57 -0
  29. package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +452 -0
  30. package/template-workspace/patches/effect-schema-error-type-id.patch +18 -0
  31. package/template-workspace/pnpm-workspace.yaml.handlebars +8 -0
  32. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +87 -5
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.5.0-ultramodern.2",
24
+ "version": "3.5.0-ultramodern.20",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/esm-node/index.js",
27
27
  "bin": {
@@ -77,7 +77,7 @@
77
77
  "@modern-js/codesmith": "2.6.9",
78
78
  "oxfmt": "0.56.0",
79
79
  "ultracite": "7.8.3",
80
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.2"
80
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.20"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@rslib/core": "0.23.1",
@@ -99,6 +99,6 @@
99
99
  "test": "rm -rf dist && rslib build -c rslibconfig.mts && rstest --passWithNoTests"
100
100
  },
101
101
  "ultramodern": {
102
- "frameworkVersion": "3.5.0-ultramodern.2"
102
+ "frameworkVersion": "3.5.0-ultramodern.20"
103
103
  }
104
104
  }
@@ -1,8 +1,11 @@
1
1
  node_modules/
2
2
  dist/
3
+ dist-cloudflare/
3
4
  build/
4
5
  .modernjs/cache/
5
6
  .modern-js/
6
7
  **/.modern-js/
8
+ .mf/
9
+ **/.mf/
7
10
  .modernjs/agent-reference-repos-tmp/
8
11
  .codex/reports/
@@ -117,19 +117,21 @@ regenerating or updating the workspace.
117
117
  | `ULTRAMODERN_ASSET_PREFIX` | UltraModern compatibility asset prefix | Modern/Rspack-emitted asset URLs when `MODERN_ASSET_PREFIX` is unset |
118
118
  | `ULTRAMODERN_PUBLIC_URL_<APP_ID>` | Per-app deployment/proof URL | Cloudflare proof inputs and Module Federation remote URLs |
119
119
 
120
- Asset URLs use this precedence: `MODERN_ASSET_PREFIX` →
121
- `ULTRAMODERN_ASSET_PREFIX` → origin-relative `/`. `MODERN_PUBLIC_SITE_URL` is
122
- canonical/SEO-only and must not be used as an asset-prefix fallback.
120
+ Shell asset URLs use this precedence: `MODERN_ASSET_PREFIX` →
121
+ `ULTRAMODERN_ASSET_PREFIX` → origin-relative `/`. Module Federation remotes use
122
+ the same env precedence, then fall back to their per-app public origin:
123
+ configured public URL, inferred workers.dev URL, or local dev port.
124
+ `MODERN_PUBLIC_SITE_URL` is canonical/SEO-only and must not be used as an
125
+ asset-prefix fallback.
123
126
  SEO output uses `MODERN_PUBLIC_SITE_URL`; if it is unset, generated local and
124
127
  preview outputs remain non-public until deployment proof provides explicit
125
128
  public URLs.
126
129
 
127
- Without public URLs configured, asset paths are origin-relative (`/`), and the
128
- dev server uses `dev.assetPrefix: '/'` so apps work through tunnels and
129
- reverse proxies (ngrok, cloudflared) without triggering Chrome's Local Network
130
- Access prompt or mixed-content errors. Shell-only workspaces can set
131
- `MODERN_PUBLIC_SITE_URL` for SEO output without changing where assets load
132
- from.
130
+ Without public URLs configured, shell asset paths are origin-relative (`/`).
131
+ Remote dev manifests publish their own local origin so host shells load
132
+ `remoteEntry.js` and exposed chunks from the remote dev server. Shell-only
133
+ workspaces can set `MODERN_PUBLIC_SITE_URL` for SEO output without changing
134
+ where assets load from.
133
135
 
134
136
  ## Cloudflare Proof
135
137
 
@@ -169,19 +171,23 @@ in API modules.
169
171
  Generated pnpm overrides pin the framework-compatible Effect cohort. Keep
170
172
  `effect` and `@effect/vitest` aligned with `pnpm-workspace.yaml`; do not add
171
173
  new direct package-level Effect versions unless the whole UltraModern cohort is
172
- upgraded.
174
+ upgraded. The generated pnpm policy intentionally excludes the matching
175
+ `effect` and `@effect/opentelemetry` cohort versions from the
176
+ minimum-release-age and no-downgrade checks while Effect's beta publishes move
177
+ from trusted-publisher metadata to provenance attestations.
173
178
 
174
179
  For older generated workspaces, run the framework migration command first:
175
180
 
176
181
  ```bash
177
- pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.2 ultramodern \
178
- migrate-strict-effect --version 3.5.0-ultramodern.2
182
+ pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.10 ultramodern \
183
+ migrate-strict-effect --version 3.5.0-ultramodern.10
179
184
  pnpm api:check
180
185
  pnpm contract:check
181
186
  ```
182
187
 
183
188
  The command updates generated package-source metadata, Modern package aliases,
184
- direct API topology metadata, and the lockfile. Remaining failures are source
189
+ framework-owned toolchain pins, direct API topology metadata, strict Effect pnpm
190
+ overrides/trust policy, and the lockfile. Remaining failures are source
185
191
  migration work; fix the owning API files instead of adding compatibility shims.
186
192
 
187
193
  ## Troubleshooting
@@ -0,0 +1,140 @@
1
+ diff --git a/dist/lazy-load-component-plugin-BYI941I2.mjs b/dist/lazy-load-component-plugin-BYI941I2.mjs
2
+ index dfba15be1e9753e0f164b53cfb22feda13f39ea4..6be1b47903b9f6807bb0111df6c588f0d54ebf53 100644
3
+ --- a/dist/lazy-load-component-plugin-BYI941I2.mjs
4
+ +++ b/dist/lazy-load-component-plugin-BYI941I2.mjs
5
+ @@ -235,6 +235,41 @@ function getTargetModuleInfo(id, instance) {
6
+ remoteEntry
7
+ };
8
+ }
9
+ +function normalizeHref(href) {
10
+ + if (typeof document === "undefined") {
11
+ + return href;
12
+ + }
13
+ + try {
14
+ + return new URL(href, document.baseURI).href;
15
+ + } catch {
16
+ + return href;
17
+ + }
18
+ +}
19
+ +function isStylesheetLink(link) {
20
+ + return link.relList.contains("stylesheet") || link.rel.toLowerCase().split(/\s+/u).includes("stylesheet");
21
+ +}
22
+ +function hasStylesheetLinkInHead(href, ignoredLink) {
23
+ + if (typeof document === "undefined" || !document.head) {
24
+ + return false;
25
+ + }
26
+ + const normalizedHref = normalizeHref(href);
27
+ + return Array.from(
28
+ + document.head.querySelectorAll("link[href]")
29
+ + ).some(
30
+ + (link) => link !== ignoredLink && isStylesheetLink(link) && normalizeHref(link.href) === normalizedHref
31
+ + );
32
+ +}
33
+ +function StylesheetAsset({ href }) {
34
+ + const [shouldRender, setShouldRender] = useState(true);
35
+ + const linkRef = useRef(null);
36
+ + useEffect(() => {
37
+ + setShouldRender(!hasStylesheetLinkInHead(href, linkRef.current));
38
+ + }, [href]);
39
+ + if (!shouldRender) {
40
+ + return null;
41
+ + }
42
+ + return /* @__PURE__ */ React__default.createElement("link", { ref: linkRef, href, rel: "stylesheet", type: "text/css" });
43
+ +}
44
+ function collectSSRAssets(options) {
45
+ const {
46
+ id,
47
+ @@ -253,15 +288,19 @@ function collectSSRAssets(options) {
48
+ }
49
+ const { module: targetModule, publicPath, remoteEntry } = moduleAndPublicPath;
50
+ if (injectLink) {
51
+ + const stylesheetHrefs = /* @__PURE__ */ new Set();
52
+ [...targetModule.assets.css.sync, ...targetModule.assets.css.async].sort().forEach((file, index) => {
53
+ + const href = `${publicPath}${file}`;
54
+ + if (stylesheetHrefs.has(href)) {
55
+ + return;
56
+ + }
57
+ + stylesheetHrefs.add(href);
58
+ links.push(
59
+ /* @__PURE__ */ React__default.createElement(
60
+ - "link",
61
+ + StylesheetAsset,
62
+ {
63
+ key: `${file.split(".")[0]}_${index}`,
64
+ - href: `${publicPath}${file}`,
65
+ - rel: "stylesheet",
66
+ - type: "text/css"
67
+ + href
68
+ }
69
+ )
70
+ );
71
+ diff --git a/dist/lazy-load-component-plugin-DG4e3Tib.js b/dist/lazy-load-component-plugin-DG4e3Tib.js
72
+ index 321c9c3cd55bf9d62a6825c9186267dfe28e5e93..867e7237d5a2ea613130c7e1710cdd998924d9db 100644
73
+ --- a/dist/lazy-load-component-plugin-DG4e3Tib.js
74
+ +++ b/dist/lazy-load-component-plugin-DG4e3Tib.js
75
+ @@ -236,6 +236,41 @@ function getTargetModuleInfo(id, instance) {
76
+ remoteEntry
77
+ };
78
+ }
79
+ +function normalizeHref(href) {
80
+ + if (typeof document === "undefined") {
81
+ + return href;
82
+ + }
83
+ + try {
84
+ + return new URL(href, document.baseURI).href;
85
+ + } catch {
86
+ + return href;
87
+ + }
88
+ +}
89
+ +function isStylesheetLink(link) {
90
+ + return link.relList.contains("stylesheet") || link.rel.toLowerCase().split(/\s+/u).includes("stylesheet");
91
+ +}
92
+ +function hasStylesheetLinkInHead(href, ignoredLink) {
93
+ + if (typeof document === "undefined" || !document.head) {
94
+ + return false;
95
+ + }
96
+ + const normalizedHref = normalizeHref(href);
97
+ + return Array.from(
98
+ + document.head.querySelectorAll("link[href]")
99
+ + ).some(
100
+ + (link) => link !== ignoredLink && isStylesheetLink(link) && normalizeHref(link.href) === normalizedHref
101
+ + );
102
+ +}
103
+ +function StylesheetAsset({ href }) {
104
+ + const [shouldRender, setShouldRender] = React.useState(true);
105
+ + const linkRef = React.useRef(null);
106
+ + React.useEffect(() => {
107
+ + setShouldRender(!hasStylesheetLinkInHead(href, linkRef.current));
108
+ + }, [href]);
109
+ + if (!shouldRender) {
110
+ + return null;
111
+ + }
112
+ + return /* @__PURE__ */ React.createElement("link", { ref: linkRef, href, rel: "stylesheet", type: "text/css" });
113
+ +}
114
+ function collectSSRAssets(options) {
115
+ const {
116
+ id,
117
+ @@ -254,15 +289,19 @@ function collectSSRAssets(options) {
118
+ }
119
+ const { module: targetModule, publicPath, remoteEntry } = moduleAndPublicPath;
120
+ if (injectLink) {
121
+ + const stylesheetHrefs = /* @__PURE__ */ new Set();
122
+ [...targetModule.assets.css.sync, ...targetModule.assets.css.async].sort().forEach((file, index) => {
123
+ + const href = `${publicPath}${file}`;
124
+ + if (stylesheetHrefs.has(href)) {
125
+ + return;
126
+ + }
127
+ + stylesheetHrefs.add(href);
128
+ links.push(
129
+ /* @__PURE__ */ React.createElement(
130
+ - "link",
131
+ + StylesheetAsset,
132
+ {
133
+ key: `${file.split(".")[0]}_${index}`,
134
+ - href: `${publicPath}${file}`,
135
+ - rel: "stylesheet",
136
+ - type: "text/css"
137
+ + href
138
+ }
139
+ )
140
+ );
@@ -0,0 +1,57 @@
1
+ diff --git a/dist/cjs/cli/configPlugin.js b/dist/cjs/cli/configPlugin.js
2
+ index 0d01ad0..e5485a2 100644
3
+ --- a/dist/cjs/cli/configPlugin.js
4
+ +++ b/dist/cjs/cli/configPlugin.js
5
+ @@ -212,9 +212,12 @@ var __webpack_exports__ = {};
6
+ if (!chain.output.get('uniqueName')) chain.output.uniqueName(mfConfig.name);
7
+ const splitChunkConfig = chain.optimization.splitChunks.entries();
8
+ if (!isServer) (0, utils_namespaceObject.autoDeleteSplitChunkCacheGroups)(mfConfig, splitChunkConfig);
9
+ - if (!isServer && enableSSR && splitChunkConfig && 'object' == typeof splitChunkConfig && splitChunkConfig.cacheGroups) {
10
+ + if (!isServer && enableSSR && splitChunkConfig && 'object' == typeof splitChunkConfig && splitChunkConfig.cacheGroups && splitChunkConfig.chunks !== undefined && splitChunkConfig.chunks !== 'async') {
11
+ + const previousChunks = splitChunkConfig.chunks;
12
+ splitChunkConfig.chunks = 'async';
13
+ - external_logger_js_default().warn('splitChunks.chunks = async is not allowed with stream SSR mode, it will auto changed to "async"');
14
+ + if (void 0 !== previousChunks) {
15
+ + external_logger_js_default().warn(`splitChunks.chunks = ${previousChunks} is not allowed with stream SSR mode, it will auto changed to "async"`);
16
+ + }
17
+ }
18
+ if ((0, external_utils_js_namespaceObject.isDev)() && 'auto' === chain.output.get('publicPath')) {
19
+ var _modernjsConfig_server;
20
+ diff --git a/dist/esm/cli/configPlugin.mjs b/dist/esm/cli/configPlugin.mjs
21
+ index 3e880c8..93e4290 100644
22
+ --- a/dist/esm/cli/configPlugin.mjs
23
+ +++ b/dist/esm/cli/configPlugin.mjs
24
+ @@ -194,9 +194,12 @@ function patchBundlerConfig(options) {
25
+ if (!chain.output.get('uniqueName')) chain.output.uniqueName(mfConfig.name);
26
+ var splitChunkConfig = chain.optimization.splitChunks.entries();
27
+ if (!isServer) autoDeleteSplitChunkCacheGroups(mfConfig, splitChunkConfig);
28
+ - if (!isServer && enableSSR && splitChunkConfig && (void 0 === splitChunkConfig ? "undefined" : _type_of__(splitChunkConfig)) === 'object' && splitChunkConfig.cacheGroups) {
29
+ + if (!isServer && enableSSR && splitChunkConfig && (void 0 === splitChunkConfig ? "undefined" : _type_of__(splitChunkConfig)) === 'object' && splitChunkConfig.cacheGroups && void 0 !== splitChunkConfig.chunks && splitChunkConfig.chunks !== 'async') {
30
+ + var previousChunks = splitChunkConfig.chunks;
31
+ splitChunkConfig.chunks = 'async';
32
+ - logger.warn('splitChunks.chunks = async is not allowed with stream SSR mode, it will auto changed to "async"');
33
+ + if (void 0 !== previousChunks) {
34
+ + logger.warn("splitChunks.chunks = ".concat(previousChunks, " is not allowed with stream SSR mode, it will auto changed to \"async\""));
35
+ + }
36
+ }
37
+ if (isDev() && 'auto' === chain.output.get('publicPath')) {
38
+ var _modernjsConfig_server;
39
+ diff --git a/dist/esm-node/cli/configPlugin.mjs b/dist/esm-node/cli/configPlugin.mjs
40
+ index 722f1bb..dcac7ec 100644
41
+ --- a/dist/esm-node/cli/configPlugin.mjs
42
+ +++ b/dist/esm-node/cli/configPlugin.mjs
43
+ @@ -165,9 +165,12 @@ function patchBundlerConfig(options) {
44
+ if (!chain.output.get('uniqueName')) chain.output.uniqueName(mfConfig.name);
45
+ const splitChunkConfig = chain.optimization.splitChunks.entries();
46
+ if (!isServer) autoDeleteSplitChunkCacheGroups(mfConfig, splitChunkConfig);
47
+ - if (!isServer && enableSSR && splitChunkConfig && 'object' == typeof splitChunkConfig && splitChunkConfig.cacheGroups) {
48
+ + if (!isServer && enableSSR && splitChunkConfig && 'object' == typeof splitChunkConfig && splitChunkConfig.cacheGroups && splitChunkConfig.chunks !== undefined && splitChunkConfig.chunks !== 'async') {
49
+ + const previousChunks = splitChunkConfig.chunks;
50
+ splitChunkConfig.chunks = 'async';
51
+ - logger.warn('splitChunks.chunks = async is not allowed with stream SSR mode, it will auto changed to "async"');
52
+ + if (void 0 !== previousChunks) {
53
+ + logger.warn(`splitChunks.chunks = ${previousChunks} is not allowed with stream SSR mode, it will auto changed to "async"`);
54
+ + }
55
+ }
56
+ if (isDev() && 'auto' === chain.output.get('publicPath')) {
57
+ var _modernjsConfig_server;