@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.4 → 3.9.0-ultramodern.5

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 (93) hide show
  1. package/dist/cjs/backend-federation-build/codegen.js +256 -0
  2. package/dist/cjs/backend-federation-build/config.js +204 -0
  3. package/dist/cjs/backend-federation-build/emit.js +150 -0
  4. package/dist/cjs/backend-federation-build/index.js +53 -0
  5. package/dist/cjs/build-config/build-environment.js +268 -0
  6. package/dist/cjs/build-config/precompress/plugin.js +124 -0
  7. package/dist/cjs/build-config/precompress/types.js +18 -0
  8. package/dist/cjs/build-config/public.js +46 -0
  9. package/dist/cjs/cloudflare/constants.js +1 -1
  10. package/dist/cjs/cloudflare/delivery-unit.js +9 -9
  11. package/dist/cjs/cloudflare-output-verifier/identity.js +6 -6
  12. package/dist/cjs/config.js +29 -1
  13. package/dist/cjs/deploy-output/npmAliases.js +226 -0
  14. package/dist/cjs/deploy-output/plugin.js +76 -0
  15. package/dist/cjs/deploy-output/target.js +48 -0
  16. package/dist/cjs/native-type-checker.js +6 -3
  17. package/dist/cjs/release-envelope/framework-output.js +27 -27
  18. package/dist/cjs/release-envelope/plugin.js +2 -1
  19. package/dist/esm/backend-federation-build/codegen.mjs +205 -0
  20. package/dist/esm/backend-federation-build/config.mjs +128 -0
  21. package/dist/esm/backend-federation-build/emit.mjs +99 -0
  22. package/dist/esm/backend-federation-build/index.mjs +1 -0
  23. package/dist/esm/build-config/build-environment.mjs +224 -0
  24. package/dist/esm/build-config/precompress/plugin.mjs +76 -0
  25. package/dist/esm/build-config/precompress/types.mjs +0 -0
  26. package/dist/esm/build-config/public.mjs +1 -0
  27. package/dist/esm/cloudflare/constants.mjs +1 -1
  28. package/dist/esm/cloudflare/delivery-unit.mjs +1 -1
  29. package/dist/esm/cloudflare-output-verifier/identity.mjs +1 -1
  30. package/dist/esm/config.mjs +1 -0
  31. package/dist/esm/deploy-output/npmAliases.mjs +174 -0
  32. package/dist/esm/deploy-output/plugin.mjs +28 -0
  33. package/dist/esm/deploy-output/target.mjs +10 -0
  34. package/dist/esm/native-type-checker.mjs +6 -3
  35. package/dist/esm/release-envelope/framework-output.mjs +1 -1
  36. package/dist/esm/release-envelope/plugin.mjs +2 -1
  37. package/dist/esm-node/backend-federation-build/codegen.mjs +206 -0
  38. package/dist/esm-node/backend-federation-build/config.mjs +129 -0
  39. package/dist/esm-node/backend-federation-build/emit.mjs +100 -0
  40. package/dist/esm-node/backend-federation-build/index.mjs +2 -0
  41. package/dist/esm-node/build-config/build-environment.mjs +225 -0
  42. package/dist/esm-node/build-config/precompress/plugin.mjs +77 -0
  43. package/dist/esm-node/build-config/precompress/types.mjs +1 -0
  44. package/dist/esm-node/build-config/public.mjs +2 -0
  45. package/dist/esm-node/cloudflare/constants.mjs +1 -1
  46. package/dist/esm-node/cloudflare/delivery-unit.mjs +1 -1
  47. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +1 -1
  48. package/dist/esm-node/config.mjs +1 -0
  49. package/dist/esm-node/deploy-output/npmAliases.mjs +175 -0
  50. package/dist/esm-node/deploy-output/plugin.mjs +31 -0
  51. package/dist/esm-node/deploy-output/target.mjs +11 -0
  52. package/dist/esm-node/native-type-checker.mjs +6 -3
  53. package/dist/esm-node/release-envelope/framework-output.mjs +1 -1
  54. package/dist/esm-node/release-envelope/plugin.mjs +2 -1
  55. package/dist/types/backend-federation-build/codegen.d.ts +86 -0
  56. package/dist/types/backend-federation-build/config.d.ts +103 -0
  57. package/dist/types/backend-federation-build/emit.d.ts +21 -0
  58. package/dist/types/backend-federation-build/index.d.ts +2 -0
  59. package/dist/types/build-config/build-environment.d.ts +22 -0
  60. package/dist/types/build-config/precompress/plugin.d.ts +4 -0
  61. package/dist/types/build-config/precompress/types.d.ts +19 -0
  62. package/dist/types/build-config/public.d.ts +2 -0
  63. package/dist/types/cloudflare/constants.d.ts +1 -1
  64. package/dist/types/cloudflare/delivery-unit.d.ts +1 -1
  65. package/dist/types/cloudflare/worker-manifest.d.ts +2 -2
  66. package/dist/types/cloudflare-output-verifier/identity.d.ts +1 -1
  67. package/dist/types/config.d.ts +2 -0
  68. package/dist/types/deploy-output/npmAliases.d.ts +14 -0
  69. package/dist/types/deploy-output/plugin.d.ts +15 -0
  70. package/dist/types/deploy-output/target.d.ts +6 -0
  71. package/dist/types/precompress/LICENSE +20 -0
  72. package/dist/types/precompress/index.d.ts +76 -0
  73. package/package.json +50 -8
  74. package/src/backend-federation-build/codegen.ts +245 -0
  75. package/src/backend-federation-build/config.ts +376 -0
  76. package/src/backend-federation-build/emit.ts +205 -0
  77. package/src/backend-federation-build/index.ts +2 -0
  78. package/src/build-config/build-environment.ts +404 -0
  79. package/src/build-config/precompress/plugin.ts +140 -0
  80. package/src/build-config/precompress/types.ts +26 -0
  81. package/src/build-config/public.ts +6 -0
  82. package/src/cloudflare/constants.ts +1 -1
  83. package/src/cloudflare/delivery-unit.ts +2 -2
  84. package/src/cloudflare/worker-manifest.ts +1 -0
  85. package/src/cloudflare-builder.ts +1 -0
  86. package/src/cloudflare-output-verifier/identity.ts +1 -1
  87. package/src/config.ts +7 -0
  88. package/src/deploy-output/npmAliases.ts +333 -0
  89. package/src/deploy-output/plugin.ts +49 -0
  90. package/src/deploy-output/target.ts +23 -0
  91. package/src/native-type-checker.ts +11 -3
  92. package/src/release-envelope/framework-output.ts +1 -1
  93. package/src/release-envelope/plugin.ts +5 -1
@@ -0,0 +1,333 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ type PackageJson = {
5
+ name?: string;
6
+ version?: string;
7
+ dependencies?: Record<string, string>;
8
+ optionalDependencies?: Record<string, string>;
9
+ [key: string]: unknown;
10
+ };
11
+
12
+ type PackageRecord = {
13
+ directory: string;
14
+ packageJson: PackageJson;
15
+ };
16
+
17
+ export type NpmAlias = {
18
+ aliasName: string;
19
+ targetName: string;
20
+ targetVersion: string;
21
+ };
22
+
23
+ const dependencyKeys = ['dependencies', 'optionalDependencies'] as const;
24
+ const exactVersionPattern =
25
+ /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
26
+
27
+ async function readPackageJson(filePath: string): Promise<PackageJson> {
28
+ return JSON.parse(await fs.readFile(filePath, 'utf8')) as PackageJson;
29
+ }
30
+
31
+ function parseNpmAlias(
32
+ aliasName: string,
33
+ specifier: string,
34
+ ): NpmAlias | undefined {
35
+ if (!specifier.startsWith('npm:')) {
36
+ return undefined;
37
+ }
38
+
39
+ const target = specifier.slice('npm:'.length);
40
+ const versionSeparator = target.lastIndexOf('@');
41
+ if (versionSeparator <= 0 || versionSeparator === target.length - 1) {
42
+ throw new Error(
43
+ `Invalid npm alias specifier for ${aliasName}: ${specifier}`,
44
+ );
45
+ }
46
+
47
+ return {
48
+ aliasName,
49
+ targetName: target.slice(0, versionSeparator),
50
+ targetVersion: target.slice(versionSeparator + 1),
51
+ };
52
+ }
53
+
54
+ function aliasesFromPackageJson(packageJson: PackageJson): NpmAlias[] {
55
+ const aliases: NpmAlias[] = [];
56
+ for (const dependencyKey of dependencyKeys) {
57
+ for (const [aliasName, specifier] of Object.entries(
58
+ packageJson[dependencyKey] ?? {},
59
+ )) {
60
+ const alias = parseNpmAlias(aliasName, specifier);
61
+ if (alias) {
62
+ aliases.push(alias);
63
+ }
64
+ }
65
+ }
66
+ return aliases;
67
+ }
68
+
69
+ async function collectPackageRecords(
70
+ nodeModulesDirectory: string,
71
+ ): Promise<PackageRecord[]> {
72
+ const records: PackageRecord[] = [];
73
+ const visitedNodeModules = new Set<string>();
74
+
75
+ const visitPackage = async (directory: string) => {
76
+ const packageJsonPath = path.join(directory, 'package.json');
77
+ try {
78
+ const packageJson = await readPackageJson(packageJsonPath);
79
+ if (packageJson.name && packageJson.version) {
80
+ records.push({ directory, packageJson });
81
+ }
82
+ } catch (error) {
83
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
84
+ throw error;
85
+ }
86
+ }
87
+ await visitNodeModules(path.join(directory, 'node_modules'));
88
+ };
89
+
90
+ const visitScope = async (scopeDirectory: string) => {
91
+ const entries = await fs.readdir(scopeDirectory, { withFileTypes: true });
92
+ for (const entry of entries) {
93
+ if (entry.isDirectory()) {
94
+ await visitPackage(path.join(scopeDirectory, entry.name));
95
+ }
96
+ }
97
+ };
98
+
99
+ const visitNdepeStore = async (storeDirectory: string) => {
100
+ const entries = await fs.readdir(storeDirectory, { withFileTypes: true });
101
+ for (const entry of entries) {
102
+ if (entry.isDirectory()) {
103
+ const entryPath = path.join(storeDirectory, entry.name);
104
+ if (entry.name.startsWith('@')) {
105
+ // ndepe preserves the slash in a scoped package name, so a
106
+ // multi-version package is stored as
107
+ // .ndepe/@scope/package@version/node_modules/@scope/package.
108
+ await visitScope(entryPath);
109
+ } else {
110
+ await visitNodeModules(path.join(entryPath, 'node_modules'));
111
+ }
112
+ }
113
+ }
114
+ };
115
+
116
+ const visitNodeModules = async (directory: string): Promise<void> => {
117
+ const resolvedDirectory = path.resolve(directory);
118
+ if (visitedNodeModules.has(resolvedDirectory)) {
119
+ return;
120
+ }
121
+ visitedNodeModules.add(resolvedDirectory);
122
+
123
+ let entries;
124
+ try {
125
+ entries = await fs.readdir(directory, { withFileTypes: true });
126
+ } catch (error) {
127
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
128
+ return;
129
+ }
130
+ throw error;
131
+ }
132
+
133
+ for (const entry of entries) {
134
+ if (!entry.isDirectory()) {
135
+ continue;
136
+ }
137
+ const entryPath = path.join(directory, entry.name);
138
+ if (entry.name === '.ndepe') {
139
+ await visitNdepeStore(entryPath);
140
+ } else if (entry.name.startsWith('@')) {
141
+ await visitScope(entryPath);
142
+ } else {
143
+ await visitPackage(entryPath);
144
+ }
145
+ }
146
+ };
147
+
148
+ await visitNodeModules(nodeModulesDirectory);
149
+ return records;
150
+ }
151
+
152
+ async function resolveAliasTarget(
153
+ ownerDirectory: string,
154
+ alias: NpmAlias,
155
+ outputDirectory: string,
156
+ ): Promise<PackageRecord | undefined> {
157
+ let directory = ownerDirectory;
158
+ const outputRoot = path.resolve(outputDirectory);
159
+ const resolvedOutputRoot = await fs.realpath(outputRoot);
160
+ while (
161
+ directory === outputRoot ||
162
+ directory.startsWith(`${outputRoot}${path.sep}`)
163
+ ) {
164
+ const candidate = path.join(directory, 'node_modules', alias.targetName);
165
+ try {
166
+ const resolvedCandidate = await fs.realpath(candidate);
167
+ if (
168
+ resolvedCandidate !== resolvedOutputRoot &&
169
+ !resolvedCandidate.startsWith(`${resolvedOutputRoot}${path.sep}`)
170
+ ) {
171
+ throw new Error(
172
+ `Cannot preserve npm alias ${alias.aliasName}: emitted target ${candidate} resolves outside deployment output to ${resolvedCandidate}`,
173
+ );
174
+ }
175
+ const packageJson = await readPackageJson(
176
+ path.join(candidate, 'package.json'),
177
+ );
178
+ if (packageJson.name !== alias.targetName || !packageJson.version) {
179
+ throw new Error(
180
+ `npm alias ${alias.aliasName} resolved ${candidate} to unexpected package ${String(packageJson.name)}`,
181
+ );
182
+ }
183
+ if (
184
+ exactVersionPattern.test(alias.targetVersion) &&
185
+ packageJson.version !== alias.targetVersion
186
+ ) {
187
+ return undefined;
188
+ }
189
+ // Keep the lexical path inside .output. realpath can cross an operating
190
+ // system alias such as macOS /var -> /private/var, which would turn the
191
+ // deployment link into a non-relocatable path outside the artifact.
192
+ return { directory: candidate, packageJson };
193
+ } catch (error) {
194
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
195
+ throw error;
196
+ }
197
+ }
198
+ if (directory === outputRoot) {
199
+ break;
200
+ }
201
+ directory = path.dirname(directory);
202
+ }
203
+ return undefined;
204
+ }
205
+
206
+ async function ensureAliasLink(
207
+ ownerDirectory: string,
208
+ alias: NpmAlias,
209
+ target: PackageRecord,
210
+ ) {
211
+ const aliasPath = path.join(ownerDirectory, 'node_modules', alias.aliasName);
212
+ await fs.mkdir(path.dirname(aliasPath), { recursive: true });
213
+
214
+ try {
215
+ const [existing, expected] = await Promise.all([
216
+ fs.realpath(aliasPath),
217
+ fs.realpath(target.directory),
218
+ ]);
219
+ if (existing === expected) {
220
+ return;
221
+ }
222
+ throw new Error(
223
+ `Cannot preserve npm alias ${alias.aliasName}: ${aliasPath} already resolves to ${existing}, expected ${expected}`,
224
+ );
225
+ } catch (error) {
226
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
227
+ throw error;
228
+ }
229
+ }
230
+
231
+ const linkTarget = path.relative(path.dirname(aliasPath), target.directory);
232
+ await fs.symlink(linkTarget, aliasPath, 'dir');
233
+ }
234
+
235
+ function aliasSpecifier(alias: NpmAlias) {
236
+ return `npm:${alias.targetName}@${alias.targetVersion}`;
237
+ }
238
+
239
+ export async function readPackageIdentity(
240
+ entryPath: string,
241
+ ): Promise<{ name: string; version: string }> {
242
+ let directory = path.dirname(entryPath);
243
+ const root = path.parse(directory).root;
244
+ while (directory !== root) {
245
+ try {
246
+ const packageJson = await readPackageJson(
247
+ path.join(directory, 'package.json'),
248
+ );
249
+ if (packageJson.name && packageJson.version) {
250
+ return { name: packageJson.name, version: packageJson.version };
251
+ }
252
+ } catch (error) {
253
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
254
+ throw error;
255
+ }
256
+ }
257
+ directory = path.dirname(directory);
258
+ }
259
+ throw new Error(
260
+ `Cannot find owning package for deployment entry ${entryPath}`,
261
+ );
262
+ }
263
+
264
+ export async function preserveNpmAliases({
265
+ appDirectory,
266
+ outputDirectory,
267
+ implicitAliases = [],
268
+ }: {
269
+ appDirectory: string;
270
+ outputDirectory: string;
271
+ implicitAliases?: NpmAlias[];
272
+ }) {
273
+ const records = await collectPackageRecords(
274
+ path.join(outputDirectory, 'node_modules'),
275
+ );
276
+
277
+ const appPackageJson = await readPackageJson(
278
+ path.join(appDirectory, 'package.json'),
279
+ );
280
+ const rootAliases = [
281
+ ...aliasesFromPackageJson(appPackageJson),
282
+ ...implicitAliases,
283
+ ].filter(alias => alias.aliasName !== alias.targetName);
284
+
285
+ const emittedRootAliases: NpmAlias[] = [];
286
+ for (const alias of rootAliases) {
287
+ const target = await resolveAliasTarget(
288
+ outputDirectory,
289
+ alias,
290
+ outputDirectory,
291
+ );
292
+ if (!target) {
293
+ if (implicitAliases.includes(alias)) {
294
+ throw new Error(
295
+ `Cannot preserve implicit npm alias ${alias.aliasName}: emitted target ${alias.targetName}@${alias.targetVersion} is missing`,
296
+ );
297
+ }
298
+ continue;
299
+ }
300
+ await ensureAliasLink(outputDirectory, alias, target);
301
+ emittedRootAliases.push(alias);
302
+ }
303
+
304
+ for (const record of records) {
305
+ for (const alias of aliasesFromPackageJson(record.packageJson).filter(
306
+ candidate => candidate.aliasName !== candidate.targetName,
307
+ )) {
308
+ const target = await resolveAliasTarget(
309
+ record.directory,
310
+ alias,
311
+ outputDirectory,
312
+ );
313
+ if (!target) {
314
+ // ndepe emits only runtime-traced packages. A package manifest can
315
+ // still declare type-only or otherwise unused aliases whose targets
316
+ // correctly are not part of this deployment output.
317
+ continue;
318
+ }
319
+ await ensureAliasLink(record.directory, alias, target);
320
+ }
321
+ }
322
+
323
+ const outputPackageJsonPath = path.join(outputDirectory, 'package.json');
324
+ const outputPackageJson = await readPackageJson(outputPackageJsonPath);
325
+ outputPackageJson.dependencies ??= {};
326
+ for (const alias of emittedRootAliases) {
327
+ outputPackageJson.dependencies[alias.aliasName] = aliasSpecifier(alias);
328
+ }
329
+ await fs.writeFile(
330
+ outputPackageJsonPath,
331
+ `${JSON.stringify(outputPackageJson, null, 2)}\n`,
332
+ );
333
+ }
@@ -0,0 +1,49 @@
1
+ import { createRequire } from 'node:module';
2
+ import path from 'node:path';
3
+ import { preserveNpmAliases, readPackageIdentity } from './npmAliases';
4
+ import {
5
+ type DeployOutputConfig,
6
+ resolveDeployTarget as defaultResolveDeployTarget,
7
+ } from './target';
8
+
9
+ export interface DeployOutputPluginApi {
10
+ getAppContext(): { appDirectory: string; metaName: string };
11
+ getNormalizedConfig(): DeployOutputConfig;
12
+ onAfterDeploy(handler: () => Promise<void>): void;
13
+ }
14
+
15
+ export const createDeployOutputAliasesPlugin = ({
16
+ resolveDeployTarget = defaultResolveDeployTarget,
17
+ }: {
18
+ resolveDeployTarget?: (config: DeployOutputConfig) => string;
19
+ } = {}) => ({
20
+ name: '@modern-js/deploy-output-aliases',
21
+ setup(api: DeployOutputPluginApi) {
22
+ api.onAfterDeploy(async () => {
23
+ const { appDirectory, metaName } = api.getAppContext();
24
+ const config = api.getNormalizedConfig();
25
+ if (
26
+ (metaName !== 'modern-js' &&
27
+ !config.deploy?.target &&
28
+ !process.env.MODERNJS_DEPLOY) ||
29
+ resolveDeployTarget(config) !== 'node'
30
+ ) {
31
+ return;
32
+ }
33
+
34
+ const entry = createRequire(__filename).resolve('@modern-js/prod-server');
35
+ const identity = await readPackageIdentity(entry);
36
+ await preserveNpmAliases({
37
+ appDirectory,
38
+ outputDirectory: path.join(appDirectory, '.output'),
39
+ implicitAliases: [
40
+ {
41
+ aliasName: '@modern-js/prod-server',
42
+ targetName: identity.name,
43
+ targetVersion: identity.version,
44
+ },
45
+ ],
46
+ });
47
+ });
48
+ },
49
+ });
@@ -0,0 +1,23 @@
1
+ import { provider } from 'std-env';
2
+
3
+ export interface DeployOutputConfig {
4
+ deploy?: { target?: string };
5
+ }
6
+
7
+ const providerDeployTargets: Partial<Record<string, string>> = {
8
+ vercel: 'vercel',
9
+ netlify: 'netlify',
10
+ cloudflare: 'cloudflare',
11
+ cloudflare_pages: 'cloudflare',
12
+ cloudflare_workers: 'cloudflare',
13
+ };
14
+
15
+ export const resolveDeployTarget = (
16
+ config: DeployOutputConfig,
17
+ envDeployTarget = process.env.MODERNJS_DEPLOY,
18
+ detectedProvider = provider,
19
+ ): string =>
20
+ config.deploy?.target ||
21
+ envDeployTarget ||
22
+ (detectedProvider ? providerDeployTargets[detectedProvider] : undefined) ||
23
+ 'node';
@@ -1,4 +1,5 @@
1
1
  import { execFile } from 'node:child_process';
2
+ import { realpathSync } from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { promisify } from 'node:util';
4
5
  import type { Rspack, RspackChain } from '@rsbuild/core';
@@ -6,6 +7,10 @@ import type { Rspack, RspackChain } from '@rsbuild/core';
6
7
  const execute = promisify(execFile);
7
8
  const name = 'UltramodernNativeTypeChecker';
8
9
 
10
+ // Windows file events expand 8.3 aliases; register the same native path spelling.
11
+ const watchDependencyPath = (file: string): string =>
12
+ process.platform === 'win32' ? realpathSync.native(file) : file;
13
+
9
14
  export class UltramodernNativeTypeChecker {
10
15
  constructor(
11
16
  private readonly options: {
@@ -32,8 +37,9 @@ export class UltramodernNativeTypeChecker {
32
37
  return result.stdout;
33
38
  } catch (cause) {
34
39
  const output = cause as { stdout?: string; stderr?: string };
40
+ const diagnostics = `${output.stdout ?? ''}${output.stderr ?? ''}`;
35
41
  throw new Error(
36
- `${name} failed:\n${output.stdout ?? ''}${output.stderr ?? ''}`,
42
+ `${name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}`,
37
43
  { cause },
38
44
  );
39
45
  }
@@ -70,11 +76,13 @@ export class UltramodernNativeTypeChecker {
70
76
 
71
77
  apply(compiler: Rspack.Compiler): void {
72
78
  compiler.hooks.thisCompilation.tap(name, compilation => {
73
- compilation.fileDependencies.add(this.options.configFile);
74
79
  compilation.hooks.processAssets.tapPromise(name, async () => {
75
80
  try {
81
+ compilation.fileDependencies.add(
82
+ watchDependencyPath(this.options.configFile),
83
+ );
76
84
  for (const file of await this.watchInputs())
77
- compilation.fileDependencies.add(file);
85
+ compilation.fileDependencies.add(watchDependencyPath(file));
78
86
  await this.check();
79
87
  } catch (cause) {
80
88
  compilation.errors.push(
@@ -7,7 +7,7 @@ import {
7
7
  isUltramodernBuildArtifact,
8
8
  ULTRAMODERN_BUILD_ARTIFACT_FILE,
9
9
  type UltramodernBuildArtifact,
10
- } from '@modern-js/utils/universal';
10
+ } from '@modern-js/backend-federation-contracts';
11
11
  import {
12
12
  canonicalSerializeMicroVerticalReleaseEnvelope,
13
13
  createMicroVerticalReleaseEnvelope,
@@ -67,7 +67,11 @@ export const createUltramodernReleaseEnvelopePlugin = <
67
67
  }): ReleaseEnvelopePlugin<Config> => {
68
68
  return {
69
69
  name: '@modern-js/ultramodern-release-envelope',
70
- pre: ['@modern-js/backend-federation-build', '@modern-js/plugin-bff'],
70
+ pre: [
71
+ '@modern-js/backend-federation-build',
72
+ '@modern-js/plugin-bff',
73
+ '@modern-js/deploy-output-aliases',
74
+ ],
71
75
  post: ['@modern-js/plugin-deploy'],
72
76
  setup(api) {
73
77
  const emitBuildEnvelope = async (target: 'node' | 'cloudflare') => {