@module-federation/treeshake-server 0.0.1-alpha.0
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 +71 -0
- package/bin/treeshake-server.js +35 -0
- package/dist/adapters/createAdapterDeps.d.ts +10 -0
- package/dist/adapters/local/adapter.d.ts +10 -0
- package/dist/adapters/local/index.d.ts +3 -0
- package/dist/adapters/local/localObjectStore.d.ts +13 -0
- package/dist/adapters/registry.d.ts +7 -0
- package/dist/adapters/types.d.ts +74 -0
- package/dist/app.d.ts +18 -0
- package/dist/cli/ossEnv.d.ts +18 -0
- package/dist/domain/build/constant.d.ts +1 -0
- package/dist/domain/build/normalize-config.d.ts +22 -0
- package/dist/domain/build/retrieve-global-name.d.ts +1 -0
- package/dist/domain/build/schema.d.ts +31 -0
- package/dist/domain/upload/constant.d.ts +2 -0
- package/dist/domain/upload/retrieve-cdn-path.d.ts +4 -0
- package/dist/frontend/adapter/index.d.ts +13 -0
- package/dist/frontend/adapter/index.js +128 -0
- package/dist/frontend/adapter/index.mjs +83 -0
- package/dist/frontend/favicon.ico +0 -0
- package/dist/frontend/index.html +1 -0
- package/dist/frontend/static/css/index.16175e0f.css +1 -0
- package/dist/frontend/static/js/296.084d1b43.js +2 -0
- package/dist/frontend/static/js/296.084d1b43.js.LICENSE.txt +16 -0
- package/dist/frontend/static/js/async/873.21368adc.js +2 -0
- package/dist/frontend/static/js/async/951.ec9191e2.js +12 -0
- package/dist/frontend/static/js/async/951.ec9191e2.js.LICENSE.txt +6 -0
- package/dist/frontend/static/js/async/987.86ff6794.js +2 -0
- package/dist/frontend/static/js/async/987.86ff6794.js.LICENSE.txt +6 -0
- package/dist/frontend/static/js/index.5488f626.js +88 -0
- package/dist/frontend/static/js/lib-react.c59642e3.js +2 -0
- package/dist/frontend/static/js/lib-react.c59642e3.js.LICENSE.txt +39 -0
- package/dist/frontend/static/js/lib-router.75e1e689.js +4 -0
- package/dist/frontend/static/js/lib-router.75e1e689.js.LICENSE.txt +10 -0
- package/dist/http/env.d.ts +10 -0
- package/dist/http/middlewares/di.middleware.d.ts +4 -0
- package/dist/http/middlewares/logger.middleware.d.ts +3 -0
- package/dist/http/routes/build.d.ts +3 -0
- package/dist/http/routes/index.d.ts +2 -0
- package/dist/http/routes/maintenance.d.ts +3 -0
- package/dist/http/routes/static.d.ts +5 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1028 -0
- package/dist/index.mjs +957 -0
- package/dist/infra/logger.d.ts +6 -0
- package/dist/nodeServer.d.ts +7 -0
- package/dist/ports/objectStore.d.ts +1 -0
- package/dist/ports/projectPublisher.d.ts +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +1160 -0
- package/dist/server.mjs +1126 -0
- package/dist/services/buildService.d.ts +8 -0
- package/dist/services/cacheService.d.ts +15 -0
- package/dist/services/pnpmMaintenance.d.ts +4 -0
- package/dist/services/uploadService.d.ts +36 -0
- package/dist/template/re-shake-share/Collect.js +115 -0
- package/dist/template/re-shake-share/EmitManifest.js +49 -0
- package/dist/template/re-shake-share/index.ts +1 -0
- package/dist/template/re-shake-share/package.json +23 -0
- package/dist/template/re-shake-share/rspack.config.ts +33 -0
- package/dist/utils/runCommand.d.ts +20 -0
- package/dist/utils/runtimeEnv.d.ts +3 -0
- package/dist/utils/uploadSdk.d.ts +10 -0
- package/package.json +68 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,1160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __webpack_require__ = {};
|
|
4
|
+
(()=>{
|
|
5
|
+
__webpack_require__.n = (module)=>{
|
|
6
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
7
|
+
__webpack_require__.d(getter, {
|
|
8
|
+
a: getter
|
|
9
|
+
});
|
|
10
|
+
return getter;
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
(()=>{
|
|
14
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
15
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: definition[key]
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
})();
|
|
21
|
+
(()=>{
|
|
22
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
26
|
+
var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
|
|
27
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
28
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
29
|
+
const SERVER_VERSION = 'v0-011501';
|
|
30
|
+
const UPLOADED_DIR = '_shared-tree-shaking';
|
|
31
|
+
const external_pino_namespaceObject = require("pino");
|
|
32
|
+
var external_pino_default = /*#__PURE__*/ __webpack_require__.n(external_pino_namespaceObject);
|
|
33
|
+
const createLogger = (opts)=>external_pino_default()({
|
|
34
|
+
level: (null == opts ? void 0 : opts.level) ?? 'info',
|
|
35
|
+
formatters: {
|
|
36
|
+
level (label) {
|
|
37
|
+
return {
|
|
38
|
+
level: label
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
let logger_logger = createLogger();
|
|
44
|
+
const setLogger = (next)=>{
|
|
45
|
+
logger_logger = next;
|
|
46
|
+
};
|
|
47
|
+
async function createAdapterDeps(params) {
|
|
48
|
+
const adapterId = params.adapterId;
|
|
49
|
+
const adapter = params.registry.getAdapterById(adapterId);
|
|
50
|
+
return adapter.create(params.adapterConfig ?? {}, {
|
|
51
|
+
logger: params.logger ?? logger_logger,
|
|
52
|
+
uploadedDir: params.uploadedDir ?? UPLOADED_DIR
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function _define_property(obj, key, value) {
|
|
56
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
57
|
+
value: value,
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
writable: true
|
|
61
|
+
});
|
|
62
|
+
else obj[key] = value;
|
|
63
|
+
return obj;
|
|
64
|
+
}
|
|
65
|
+
class LocalObjectStore {
|
|
66
|
+
async exists(key) {
|
|
67
|
+
const filePath = external_node_path_default().join(this.rootDir, key.replace(/^\//, ''));
|
|
68
|
+
try {
|
|
69
|
+
const stat = await external_node_fs_default().promises.stat(filePath);
|
|
70
|
+
return stat.isFile();
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async uploadFile(localPath, key) {
|
|
76
|
+
const rel = key.replace(/^\//, '');
|
|
77
|
+
const dest = external_node_path_default().join(this.rootDir, rel);
|
|
78
|
+
await external_node_fs_default().promises.mkdir(external_node_path_default().dirname(dest), {
|
|
79
|
+
recursive: true
|
|
80
|
+
});
|
|
81
|
+
await external_node_fs_default().promises.copyFile(localPath, dest);
|
|
82
|
+
}
|
|
83
|
+
async downloadFile(key, localPath) {
|
|
84
|
+
const rel = key.replace(/^\//, '');
|
|
85
|
+
const src = external_node_path_default().join(this.rootDir, rel);
|
|
86
|
+
await external_node_fs_default().promises.mkdir(external_node_path_default().dirname(localPath), {
|
|
87
|
+
recursive: true
|
|
88
|
+
});
|
|
89
|
+
await external_node_fs_default().promises.copyFile(src, localPath);
|
|
90
|
+
}
|
|
91
|
+
publicUrl(key) {
|
|
92
|
+
return `${this.publicBaseUrl}${key.replace(/^\//, '')}`;
|
|
93
|
+
}
|
|
94
|
+
constructor(opts){
|
|
95
|
+
_define_property(this, "rootDir", void 0);
|
|
96
|
+
_define_property(this, "publicBaseUrl", void 0);
|
|
97
|
+
this.rootDir = (null == opts ? void 0 : opts.rootDir) ?? external_node_path_default().join(process.cwd(), 'log', 'static');
|
|
98
|
+
const base = (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
|
|
99
|
+
this.publicBaseUrl = base.endsWith('/') ? base : `${base}/`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function adapter_define_property(obj, key, value) {
|
|
103
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
104
|
+
value: value,
|
|
105
|
+
enumerable: true,
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true
|
|
108
|
+
});
|
|
109
|
+
else obj[key] = value;
|
|
110
|
+
return obj;
|
|
111
|
+
}
|
|
112
|
+
function envStr(env, name, fallback) {
|
|
113
|
+
const v = env[name];
|
|
114
|
+
if (void 0 === v || '' === v) return fallback;
|
|
115
|
+
return v;
|
|
116
|
+
}
|
|
117
|
+
class LocalAdapter {
|
|
118
|
+
fromEnv(env) {
|
|
119
|
+
return {
|
|
120
|
+
rootDir: envStr(env, 'LOCAL_STORE_DIR', external_node_path_default().join(process.cwd(), 'log', 'static')),
|
|
121
|
+
publicBaseUrl: envStr(env, 'LOCAL_STORE_BASE_URL', '/')
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async create(config, _context) {
|
|
125
|
+
const objectStore = new LocalObjectStore({
|
|
126
|
+
rootDir: config.rootDir,
|
|
127
|
+
publicBaseUrl: config.publicBaseUrl
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
objectStore
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
constructor(){
|
|
134
|
+
adapter_define_property(this, "id", 'local');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function createAdapterRegistry(adapters) {
|
|
138
|
+
return {
|
|
139
|
+
adapters,
|
|
140
|
+
getAdapterById: (id)=>{
|
|
141
|
+
const found = adapters.find((a)=>a.id === id);
|
|
142
|
+
if (!found) throw new Error(`Unknown ADAPTER_ID: ${id}`);
|
|
143
|
+
return found;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function createOssAdapterRegistry() {
|
|
148
|
+
return createAdapterRegistry([
|
|
149
|
+
new LocalAdapter()
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
const external_hono_namespaceObject = require("hono");
|
|
153
|
+
function contentTypeByExt(filePath) {
|
|
154
|
+
if (filePath.endsWith('.js')) return "application/javascript";
|
|
155
|
+
if (filePath.endsWith('.json')) return 'application/json';
|
|
156
|
+
return 'application/octet-stream';
|
|
157
|
+
}
|
|
158
|
+
const DEFAULT_STATIC_ROOT = external_node_path_default().join(process.cwd(), 'log', 'static');
|
|
159
|
+
async function serveLocalFile(c, rootDir) {
|
|
160
|
+
let requestPath = c.req.path;
|
|
161
|
+
try {
|
|
162
|
+
requestPath = decodeURIComponent(requestPath);
|
|
163
|
+
} catch {
|
|
164
|
+
return c.text('Not Found', 404);
|
|
165
|
+
}
|
|
166
|
+
const relPath = requestPath.replace(/^\/+/, '');
|
|
167
|
+
const rootResolved = external_node_path_default().resolve(rootDir);
|
|
168
|
+
const filePath = external_node_path_default().resolve(rootResolved, relPath);
|
|
169
|
+
if (filePath !== rootResolved && !filePath.startsWith(`${rootResolved}${external_node_path_default().sep}`)) return c.text('Not Found', 404);
|
|
170
|
+
try {
|
|
171
|
+
const buf = await external_node_fs_default().promises.readFile(filePath);
|
|
172
|
+
return new Response(buf, {
|
|
173
|
+
status: 200,
|
|
174
|
+
headers: {
|
|
175
|
+
'Content-Type': contentTypeByExt(filePath)
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
} catch {
|
|
179
|
+
return c.text('Not Found', 404);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function createStaticRoute(opts) {
|
|
183
|
+
const staticRoute = new external_hono_namespaceObject.Hono();
|
|
184
|
+
const rootDir = (null == opts ? void 0 : opts.rootDir) ?? DEFAULT_STATIC_ROOT;
|
|
185
|
+
staticRoute.get('/tree-shaking-shared/*', async (c)=>serveLocalFile(c, rootDir));
|
|
186
|
+
return staticRoute;
|
|
187
|
+
}
|
|
188
|
+
const DEFAULT_PRUNE_INTERVAL_MS = 3600000;
|
|
189
|
+
function ossEnv_envStr(env, name, fallback) {
|
|
190
|
+
const v = env[name];
|
|
191
|
+
if (void 0 === v || '' === v) return fallback;
|
|
192
|
+
return v;
|
|
193
|
+
}
|
|
194
|
+
function envNum(env, name, fallback) {
|
|
195
|
+
const v = ossEnv_envStr(env, name);
|
|
196
|
+
if (!v) return fallback;
|
|
197
|
+
const n = Number(v);
|
|
198
|
+
return Number.isFinite(n) ? n : fallback;
|
|
199
|
+
}
|
|
200
|
+
function resolveOssEnv(params) {
|
|
201
|
+
const adapterId = ossEnv_envStr(params.env, 'ADAPTER_ID', params.defaultAdapterId) ?? 'local';
|
|
202
|
+
const adapter = params.registry.getAdapterById(adapterId);
|
|
203
|
+
const adapterConfig = adapter.fromEnv ? adapter.fromEnv(params.env) : {};
|
|
204
|
+
return {
|
|
205
|
+
adapterId,
|
|
206
|
+
adapterConfig,
|
|
207
|
+
corsOrigin: ossEnv_envStr(params.env, 'CORS_ORIGIN', '*') ?? '*',
|
|
208
|
+
staticRootDir: ossEnv_envStr(params.env, 'LOCAL_STORE_DIR', DEFAULT_STATIC_ROOT) ?? DEFAULT_STATIC_ROOT,
|
|
209
|
+
logLevel: ossEnv_envStr(params.env, 'LOG_LEVEL', 'info') ?? 'info',
|
|
210
|
+
port: envNum(params.env, 'PORT', 3000),
|
|
211
|
+
hostname: ossEnv_envStr(params.env, 'HOST', '0.0.0.0') ?? '0.0.0.0',
|
|
212
|
+
pruneIntervalMs: envNum(params.env, 'PNPM_PRUNE_INTERVAL_MS', DEFAULT_PRUNE_INTERVAL_MS),
|
|
213
|
+
runtimeEnv: params.env
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const cors_namespaceObject = require("hono/cors");
|
|
217
|
+
function createDiMiddleware(deps) {
|
|
218
|
+
return async (c, next)=>{
|
|
219
|
+
c.set('objectStore', deps.objectStore);
|
|
220
|
+
if (deps.projectPublisher) c.set('projectPublisher', deps.projectPublisher);
|
|
221
|
+
await next();
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const loggerMiddleware = async (c, next)=>{
|
|
225
|
+
const category = c.req.path.split('/')[1] || 'root';
|
|
226
|
+
const child = logger_logger.child({
|
|
227
|
+
category,
|
|
228
|
+
path: c.req.path,
|
|
229
|
+
method: c.req.method
|
|
230
|
+
});
|
|
231
|
+
c.set('logger', child);
|
|
232
|
+
await next();
|
|
233
|
+
};
|
|
234
|
+
const external_node_os_namespaceObject = require("node:os");
|
|
235
|
+
var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
|
|
236
|
+
const zod_validator_namespaceObject = require("@hono/zod-validator");
|
|
237
|
+
const external_nanoid_namespaceObject = require("nanoid");
|
|
238
|
+
const external_p_limit_namespaceObject = require("p-limit");
|
|
239
|
+
var external_p_limit_default = /*#__PURE__*/ __webpack_require__.n(external_p_limit_namespaceObject);
|
|
240
|
+
const parseNormalizedKey = (key)=>{
|
|
241
|
+
const res = key.split('@');
|
|
242
|
+
return {
|
|
243
|
+
name: res.slice(0, -1).join('@'),
|
|
244
|
+
version: res[res.length - 1]
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
const normalizedKey = (name, v)=>`${name}@${v}`;
|
|
248
|
+
function normalizeConfig(config) {
|
|
249
|
+
const { shared, plugins, target, libraryType, hostName, uploadOptions } = config;
|
|
250
|
+
const commonNormalizedConfig = {
|
|
251
|
+
plugins: (null == plugins ? void 0 : plugins.sort(([a], [b])=>a.localeCompare(b))) ?? [],
|
|
252
|
+
target: Array.isArray(target) ? [
|
|
253
|
+
...target
|
|
254
|
+
].sort() : [
|
|
255
|
+
target
|
|
256
|
+
],
|
|
257
|
+
uploadOptions,
|
|
258
|
+
libraryType,
|
|
259
|
+
hostName
|
|
260
|
+
};
|
|
261
|
+
const normalizedConfig = {};
|
|
262
|
+
shared.forEach(([sharedName, version, usedExports], index)=>{
|
|
263
|
+
const key = normalizedKey(sharedName, version);
|
|
264
|
+
normalizedConfig[key] = {
|
|
265
|
+
...commonNormalizedConfig,
|
|
266
|
+
shared: shared.slice(0, index).concat(shared.slice(index + 1)).sort(([s, v, u], [b, v2, u2])=>`${s}${v}${u.sort().join('')}`.localeCompare(`${b}${v2}${u2.sort().join('')}`)),
|
|
267
|
+
usedExports
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
return normalizedConfig;
|
|
271
|
+
}
|
|
272
|
+
function extractBuildConfig(config, type) {
|
|
273
|
+
const { shared, plugins, target, libraryType, usedExports, hostName } = config;
|
|
274
|
+
return {
|
|
275
|
+
shared,
|
|
276
|
+
plugins,
|
|
277
|
+
target,
|
|
278
|
+
libraryType,
|
|
279
|
+
usedExports,
|
|
280
|
+
type,
|
|
281
|
+
hostName
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const external_zod_namespaceObject = require("zod");
|
|
285
|
+
const UploadOptionsSchema = external_zod_namespaceObject.z.object({
|
|
286
|
+
scmName: external_zod_namespaceObject.z.string(),
|
|
287
|
+
bucketName: external_zod_namespaceObject.z.string(),
|
|
288
|
+
publicFilePath: external_zod_namespaceObject.z.string(),
|
|
289
|
+
publicPath: external_zod_namespaceObject.z.string(),
|
|
290
|
+
cdnRegion: external_zod_namespaceObject.z.string()
|
|
291
|
+
});
|
|
292
|
+
const BasicSchema = external_zod_namespaceObject.z.object({
|
|
293
|
+
shared: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.tuple([
|
|
294
|
+
external_zod_namespaceObject.z.string(),
|
|
295
|
+
external_zod_namespaceObject.z.string(),
|
|
296
|
+
external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())
|
|
297
|
+
])).describe('List of plugins: [name, version, usedExports]'),
|
|
298
|
+
plugins: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.tuple([
|
|
299
|
+
external_zod_namespaceObject.z.string(),
|
|
300
|
+
external_zod_namespaceObject.z.string().optional()
|
|
301
|
+
])).describe('Specify extra build plugin names, support specify version in second item').optional(),
|
|
302
|
+
target: external_zod_namespaceObject.z.union([
|
|
303
|
+
external_zod_namespaceObject.z.string(),
|
|
304
|
+
external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())
|
|
305
|
+
]).describe('Used to configure the target environment of Rspack output and the ECMAScript version of Rspack runtime code, the same with rspack#target'),
|
|
306
|
+
libraryType: external_zod_namespaceObject.z.string(),
|
|
307
|
+
hostName: external_zod_namespaceObject.z.string().describe('The name of the host app / mf')
|
|
308
|
+
});
|
|
309
|
+
const ConfigSchema = BasicSchema.extend({
|
|
310
|
+
uploadOptions: UploadOptionsSchema.optional()
|
|
311
|
+
});
|
|
312
|
+
const CheckTreeShakingSchema = ConfigSchema.extend({
|
|
313
|
+
uploadOptions: UploadOptionsSchema.optional()
|
|
314
|
+
});
|
|
315
|
+
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
316
|
+
const promises_namespaceObject = require("node:fs/promises");
|
|
317
|
+
var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
|
|
318
|
+
const STATS_NAME = 'mf-stats.json';
|
|
319
|
+
const external_node_child_process_namespaceObject = require("node:child_process");
|
|
320
|
+
let runtimeEnv = {};
|
|
321
|
+
const setRuntimeEnv = (env)=>{
|
|
322
|
+
runtimeEnv = env;
|
|
323
|
+
};
|
|
324
|
+
const getRuntimeEnv = ()=>runtimeEnv;
|
|
325
|
+
function runCommand_define_property(obj, key, value) {
|
|
326
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
327
|
+
value: value,
|
|
328
|
+
enumerable: true,
|
|
329
|
+
configurable: true,
|
|
330
|
+
writable: true
|
|
331
|
+
});
|
|
332
|
+
else obj[key] = value;
|
|
333
|
+
return obj;
|
|
334
|
+
}
|
|
335
|
+
class CommandExecutionError extends Error {
|
|
336
|
+
constructor(command, exitCode, stdout, stderr){
|
|
337
|
+
super(`Command "${command}" exited with code ${exitCode}`), runCommand_define_property(this, "command", void 0), runCommand_define_property(this, "exitCode", void 0), runCommand_define_property(this, "stdout", void 0), runCommand_define_property(this, "stderr", void 0);
|
|
338
|
+
this.name = 'CommandExecutionError';
|
|
339
|
+
this.command = command;
|
|
340
|
+
this.exitCode = exitCode;
|
|
341
|
+
this.stdout = stdout;
|
|
342
|
+
this.stderr = stderr;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const runCommand = (command, options = {})=>new Promise((resolve, reject)=>{
|
|
346
|
+
const stdoutChunks = [];
|
|
347
|
+
const stderrChunks = [];
|
|
348
|
+
const child = (0, external_node_child_process_namespaceObject.spawn)(command, {
|
|
349
|
+
cwd: options.cwd,
|
|
350
|
+
env: {
|
|
351
|
+
...getRuntimeEnv(),
|
|
352
|
+
...options.env
|
|
353
|
+
},
|
|
354
|
+
shell: true,
|
|
355
|
+
stdio: [
|
|
356
|
+
'ignore',
|
|
357
|
+
'pipe',
|
|
358
|
+
'pipe'
|
|
359
|
+
]
|
|
360
|
+
});
|
|
361
|
+
child.stdout.on('data', (chunk)=>{
|
|
362
|
+
stdoutChunks.push(chunk.toString());
|
|
363
|
+
});
|
|
364
|
+
child.stderr.on('data', (chunk)=>{
|
|
365
|
+
stderrChunks.push(chunk.toString());
|
|
366
|
+
});
|
|
367
|
+
child.on('error', (error)=>{
|
|
368
|
+
reject(error);
|
|
369
|
+
});
|
|
370
|
+
child.on('close', (exitCode)=>{
|
|
371
|
+
const stdout = stdoutChunks.join('');
|
|
372
|
+
const stderr = stderrChunks.join('');
|
|
373
|
+
if (0 === exitCode) return void resolve({
|
|
374
|
+
stdout,
|
|
375
|
+
stderr,
|
|
376
|
+
exitCode
|
|
377
|
+
});
|
|
378
|
+
reject(new CommandExecutionError(command, exitCode ?? -1, stdout, stderr));
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
let installs = 0;
|
|
382
|
+
let pruneScheduled = false;
|
|
383
|
+
let pruning = false;
|
|
384
|
+
const DEFAULT_INTERVAL = 3600000;
|
|
385
|
+
const markInstallStart = ()=>{
|
|
386
|
+
installs++;
|
|
387
|
+
};
|
|
388
|
+
const markInstallEnd = ()=>{
|
|
389
|
+
installs = Math.max(0, installs - 1);
|
|
390
|
+
schedulePruneSoon();
|
|
391
|
+
};
|
|
392
|
+
const schedulePruneSoon = (delay = 30000)=>{
|
|
393
|
+
if (pruneScheduled) return;
|
|
394
|
+
pruneScheduled = true;
|
|
395
|
+
setTimeout(()=>{
|
|
396
|
+
pruneScheduled = false;
|
|
397
|
+
maybePrune();
|
|
398
|
+
}, delay);
|
|
399
|
+
};
|
|
400
|
+
const maybePrune = async ()=>{
|
|
401
|
+
if (pruning || installs > 0) return void schedulePruneSoon(60000);
|
|
402
|
+
pruning = true;
|
|
403
|
+
try {
|
|
404
|
+
await runCommand('pnpm store prune');
|
|
405
|
+
} catch {} finally{
|
|
406
|
+
pruning = false;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const startPeriodicPrune = (intervalMs = DEFAULT_INTERVAL)=>{
|
|
410
|
+
if (intervalMs <= 0) return;
|
|
411
|
+
setInterval(()=>{
|
|
412
|
+
maybePrune();
|
|
413
|
+
}, intervalMs);
|
|
414
|
+
};
|
|
415
|
+
const createUniqueTempDirByKey = async (key)=>{
|
|
416
|
+
const base = external_node_path_default().join(external_node_os_default().tmpdir(), `re-shake-share-${key}`);
|
|
417
|
+
let candidate = base;
|
|
418
|
+
for(;;)try {
|
|
419
|
+
await promises_default().mkdir(candidate, {
|
|
420
|
+
recursive: false
|
|
421
|
+
});
|
|
422
|
+
return candidate;
|
|
423
|
+
} catch {
|
|
424
|
+
const rand = Math.floor(1e9 * Math.random());
|
|
425
|
+
candidate = `${base}-${rand}`;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
const prepareProject = async (config, excludeShared)=>{
|
|
429
|
+
const key = (0, external_node_crypto_namespaceObject.createHash)('sha256').update(JSON.stringify(config)).digest('hex');
|
|
430
|
+
const dir = await createUniqueTempDirByKey(key);
|
|
431
|
+
const templateDir = external_node_path_default().join(__dirname, '.', 'template', 're-shake-share');
|
|
432
|
+
await promises_default().cp(templateDir, dir, {
|
|
433
|
+
recursive: true
|
|
434
|
+
});
|
|
435
|
+
const pkgPath = external_node_path_default().join(dir, 'package.json');
|
|
436
|
+
const indexPath = external_node_path_default().join(dir, 'index.ts');
|
|
437
|
+
const rspackConfigPath = external_node_path_default().join(dir, 'rspack.config.ts');
|
|
438
|
+
const [pkgContent, indexContent, rspackConfigContent] = await Promise.all([
|
|
439
|
+
promises_default().readFile(pkgPath, 'utf-8'),
|
|
440
|
+
promises_default().readFile(indexPath, 'utf-8'),
|
|
441
|
+
promises_default().readFile(rspackConfigPath, 'utf-8')
|
|
442
|
+
]);
|
|
443
|
+
const pkg = JSON.parse(pkgContent);
|
|
444
|
+
const deps = {
|
|
445
|
+
...pkg.dependencies || {}
|
|
446
|
+
};
|
|
447
|
+
const shared = {};
|
|
448
|
+
const mfConfig = {
|
|
449
|
+
name: 're_shake',
|
|
450
|
+
library: {
|
|
451
|
+
type: 'global'
|
|
452
|
+
},
|
|
453
|
+
manifest: true,
|
|
454
|
+
shared
|
|
455
|
+
};
|
|
456
|
+
let pluginImportStr = '';
|
|
457
|
+
let pluginOptionStr = '[\n';
|
|
458
|
+
let sharedImport = '';
|
|
459
|
+
Object.entries(config).forEach(([key, { plugins = [], libraryType, usedExports, hostName }], index)=>{
|
|
460
|
+
const { name, version } = parseNormalizedKey(key);
|
|
461
|
+
deps[name] = version;
|
|
462
|
+
if (!index) {
|
|
463
|
+
plugins.forEach(([pluginName, pluginVersion], pIndex)=>{
|
|
464
|
+
deps[pluginName] = pluginVersion ?? 'latest';
|
|
465
|
+
const pluginImportName = `plugin_${pIndex}`;
|
|
466
|
+
pluginImportStr += `import ${pluginImportName} from '${pluginName}';\n`;
|
|
467
|
+
pluginOptionStr += `new ${pluginImportName}(),`;
|
|
468
|
+
});
|
|
469
|
+
mfConfig.library.type = libraryType;
|
|
470
|
+
mfConfig.name = hostName;
|
|
471
|
+
}
|
|
472
|
+
shared[name] = {
|
|
473
|
+
requiredVersion: version,
|
|
474
|
+
version,
|
|
475
|
+
treeShaking: excludeShared.some(([n, v])=>n === name && v === version) ? void 0 : {
|
|
476
|
+
usedExports,
|
|
477
|
+
mode: 'server-calc'
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
sharedImport += `import shared_${index} from '${name}';\n`;
|
|
481
|
+
});
|
|
482
|
+
pluginOptionStr += '\n]';
|
|
483
|
+
pkg.dependencies = deps;
|
|
484
|
+
const newPkgContent = JSON.stringify(pkg, null, 2);
|
|
485
|
+
const sharedImportPlaceholder = "${SHARED_IMPORT}";
|
|
486
|
+
const newIndexContent = indexContent.replace(sharedImportPlaceholder, sharedImport);
|
|
487
|
+
const pluginsPlaceholder = "${ PLUGINS }";
|
|
488
|
+
const mfConfigPlaceholder = "${ MF_CONFIG }";
|
|
489
|
+
let cfg = rspackConfigContent;
|
|
490
|
+
cfg += pluginImportStr;
|
|
491
|
+
cfg = cfg.replace(pluginsPlaceholder, pluginOptionStr);
|
|
492
|
+
cfg = cfg.replace(mfConfigPlaceholder, JSON.stringify(mfConfig, null, 2));
|
|
493
|
+
await Promise.all([
|
|
494
|
+
promises_default().writeFile(pkgPath, newPkgContent),
|
|
495
|
+
promises_default().writeFile(indexPath, newIndexContent),
|
|
496
|
+
promises_default().writeFile(rspackConfigPath, cfg)
|
|
497
|
+
]);
|
|
498
|
+
return dir;
|
|
499
|
+
};
|
|
500
|
+
const installDependencies = async (cwd)=>{
|
|
501
|
+
markInstallStart();
|
|
502
|
+
try {
|
|
503
|
+
await runCommand("pnpm i --ignore-scripts --no-frozen-lockfile --prefer-offline --reporter=silent --shamefully-hoist", {
|
|
504
|
+
cwd,
|
|
505
|
+
env: {
|
|
506
|
+
npm_config_registry: 'https://registry.npmjs.org/'
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
} finally{
|
|
510
|
+
markInstallEnd();
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
const buildProject = async (cwd, type)=>{
|
|
514
|
+
const scripts = [];
|
|
515
|
+
if ('re-shake' === type) scripts.push('pnpm build');
|
|
516
|
+
else scripts.push('pnpm build:full');
|
|
517
|
+
await Promise.all(scripts.map((script)=>runCommand(script, {
|
|
518
|
+
cwd
|
|
519
|
+
})));
|
|
520
|
+
};
|
|
521
|
+
const retrieveSharedFilepaths = async (projectDir, type)=>{
|
|
522
|
+
const sharedFilepaths = [];
|
|
523
|
+
const collectSharedFilepaths = async (t)=>{
|
|
524
|
+
const dir = 'full' === t ? 'full-shared' : 'dist';
|
|
525
|
+
const distDir = external_node_path_default().join(projectDir, dir);
|
|
526
|
+
const statsContent = await promises_default().readFile(external_node_path_default().join(distDir, STATS_NAME), 'utf-8');
|
|
527
|
+
const stats = JSON.parse(statsContent);
|
|
528
|
+
stats.shared.forEach((s)=>{
|
|
529
|
+
const { name, version, fallback, fallbackName } = s;
|
|
530
|
+
if (fallback && fallbackName) {
|
|
531
|
+
var _s_usedExports;
|
|
532
|
+
const filepath = external_node_path_default().join(distDir, fallback);
|
|
533
|
+
sharedFilepaths.push({
|
|
534
|
+
name,
|
|
535
|
+
version,
|
|
536
|
+
filepath,
|
|
537
|
+
globalName: fallbackName,
|
|
538
|
+
type: t,
|
|
539
|
+
modules: s.usedExports,
|
|
540
|
+
canTreeShaking: s.canTreeShaking ?? (null == (_s_usedExports = s.usedExports) ? void 0 : _s_usedExports.length) !== 0
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
await collectSharedFilepaths(type);
|
|
546
|
+
return sharedFilepaths;
|
|
547
|
+
};
|
|
548
|
+
const runBuild = async (normalizedConfig, excludeShared, type)=>{
|
|
549
|
+
const tStart = Date.now();
|
|
550
|
+
const tmpDir = await prepareProject(normalizedConfig, excludeShared);
|
|
551
|
+
const tPrepare = Date.now();
|
|
552
|
+
logger_logger.info(`prepareProject took ${tPrepare - tStart}ms`);
|
|
553
|
+
await installDependencies(tmpDir);
|
|
554
|
+
const tInstall = Date.now();
|
|
555
|
+
logger_logger.info(`installDependencies took ${tInstall - tPrepare}ms`);
|
|
556
|
+
await buildProject(tmpDir, type);
|
|
557
|
+
const tBuild = Date.now();
|
|
558
|
+
logger_logger.info(`buildProject took ${tBuild - tInstall}ms`);
|
|
559
|
+
const sharedFilePaths = await retrieveSharedFilepaths(tmpDir, type);
|
|
560
|
+
const tRetrieve = Date.now();
|
|
561
|
+
logger_logger.info(`retrieveSharedFilepaths took ${tRetrieve - tBuild}ms`);
|
|
562
|
+
return {
|
|
563
|
+
sharedFilePaths,
|
|
564
|
+
dir: tmpDir
|
|
565
|
+
};
|
|
566
|
+
};
|
|
567
|
+
async function cleanUp(tmpDir) {
|
|
568
|
+
if (!tmpDir) return;
|
|
569
|
+
await promises_default().rm(tmpDir, {
|
|
570
|
+
recursive: true,
|
|
571
|
+
force: true
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
const external_json_stable_stringify_namespaceObject = require("json-stable-stringify");
|
|
575
|
+
var external_json_stable_stringify_default = /*#__PURE__*/ __webpack_require__.n(external_json_stable_stringify_namespaceObject);
|
|
576
|
+
const encodeName = function(name, prefix = '', withExt = false) {
|
|
577
|
+
const ext = withExt ? '.js' : '';
|
|
578
|
+
return `${prefix}${name.replace(/@/g, 'scope_').replace(/-/g, '_').replace(/\//g, '__').replace(/\./g, '')}${ext}`;
|
|
579
|
+
};
|
|
580
|
+
function retrieveGlobalName(mfName, sharedName, version) {
|
|
581
|
+
return encodeName(`${mfName}_${sharedName}_${version}`);
|
|
582
|
+
}
|
|
583
|
+
function createCacheHash(config, type) {
|
|
584
|
+
const relevant = extractBuildConfig({
|
|
585
|
+
...config,
|
|
586
|
+
usedExports: 'full' === type ? [] : config.usedExports
|
|
587
|
+
}, type);
|
|
588
|
+
const json = external_json_stable_stringify_default()(relevant);
|
|
589
|
+
if (!json) throw new Error('Can not stringify build config!');
|
|
590
|
+
return (0, external_node_crypto_namespaceObject.createHash)('sha256').update(json).digest('hex');
|
|
591
|
+
}
|
|
592
|
+
function retrieveCDNPath({ config, sharedKey, type }) {
|
|
593
|
+
const configHash = createCacheHash(config, type);
|
|
594
|
+
return `tree-shaking-shared/${SERVER_VERSION}/${sharedKey}/${configHash}.js`;
|
|
595
|
+
}
|
|
596
|
+
async function cacheService_hitCache(sharedKey, config, type, store) {
|
|
597
|
+
const cdnPath = retrieveCDNPath({
|
|
598
|
+
config,
|
|
599
|
+
sharedKey,
|
|
600
|
+
type
|
|
601
|
+
});
|
|
602
|
+
const exists = await store.exists(cdnPath);
|
|
603
|
+
return exists ? store.publicUrl(cdnPath) : null;
|
|
604
|
+
}
|
|
605
|
+
const retrieveCacheItems = async (normalizedConfig, type, store)=>{
|
|
606
|
+
const cacheItems = [];
|
|
607
|
+
const restConfig = {};
|
|
608
|
+
const excludeShared = [];
|
|
609
|
+
for (const [sharedKey, config] of Object.entries(normalizedConfig)){
|
|
610
|
+
let cache = false;
|
|
611
|
+
const { name, version } = parseNormalizedKey(sharedKey);
|
|
612
|
+
const cdnUrl = await cacheService_hitCache(sharedKey, config, type, store);
|
|
613
|
+
if (cdnUrl) {
|
|
614
|
+
cache = true;
|
|
615
|
+
cacheItems.push({
|
|
616
|
+
type,
|
|
617
|
+
name,
|
|
618
|
+
version,
|
|
619
|
+
cdnUrl,
|
|
620
|
+
globalName: retrieveGlobalName(config.hostName, name, version)
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
if (cache) excludeShared.push([
|
|
624
|
+
name,
|
|
625
|
+
version
|
|
626
|
+
]);
|
|
627
|
+
else if (config.usedExports.length || 're-shake' !== type) restConfig[sharedKey] = config;
|
|
628
|
+
else excludeShared.push([
|
|
629
|
+
name,
|
|
630
|
+
version
|
|
631
|
+
]);
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
cacheItems,
|
|
635
|
+
excludeShared,
|
|
636
|
+
restConfig
|
|
637
|
+
};
|
|
638
|
+
};
|
|
639
|
+
async function uploadToCacheStore(sharedFilePaths, normalizedConfig, store) {
|
|
640
|
+
const results = [];
|
|
641
|
+
for (const file of sharedFilePaths){
|
|
642
|
+
const { name, version, filepath, globalName, type, modules, canTreeShaking } = file;
|
|
643
|
+
try {
|
|
644
|
+
const sharedKey = normalizedKey(name, version);
|
|
645
|
+
logger_logger.info(`Uploading ${sharedKey} to CDN`);
|
|
646
|
+
const config = normalizedConfig[sharedKey];
|
|
647
|
+
const cdnPath = retrieveCDNPath({
|
|
648
|
+
config,
|
|
649
|
+
sharedKey,
|
|
650
|
+
type
|
|
651
|
+
});
|
|
652
|
+
const t0 = Date.now();
|
|
653
|
+
await store.uploadFile(filepath, cdnPath);
|
|
654
|
+
const tUpload = Date.now() - t0;
|
|
655
|
+
const cdnUrl = store.publicUrl(cdnPath);
|
|
656
|
+
const res = {
|
|
657
|
+
name: name,
|
|
658
|
+
version: version,
|
|
659
|
+
globalName: globalName,
|
|
660
|
+
cdnUrl,
|
|
661
|
+
type,
|
|
662
|
+
modules,
|
|
663
|
+
canTreeShaking
|
|
664
|
+
};
|
|
665
|
+
try {
|
|
666
|
+
const jsonFilePath = filepath.replace(/\.js$/, '.json');
|
|
667
|
+
const jsonFile = JSON.stringify(res);
|
|
668
|
+
const jsonCdnUrl = cdnPath.replace(/\.js$/, '.json');
|
|
669
|
+
await promises_default().writeFile(jsonFilePath, jsonFile);
|
|
670
|
+
await store.uploadFile(jsonFilePath, jsonCdnUrl);
|
|
671
|
+
} catch (error) {
|
|
672
|
+
logger_logger.error(`Failed to upload ${name}@${version} json file: ${error}`);
|
|
673
|
+
}
|
|
674
|
+
results.push(res);
|
|
675
|
+
logger_logger.info(`Successfully uploaded ${name}@${version} to ${cdnUrl} in ${tUpload}ms`);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
logger_logger.error(`Failed to upload ${name}@${version}: ${error}`);
|
|
678
|
+
throw error;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return results;
|
|
682
|
+
}
|
|
683
|
+
const downloadToFile = async (url, destPath)=>{
|
|
684
|
+
const res = await fetch(url);
|
|
685
|
+
if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText} - ${url}`);
|
|
686
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
687
|
+
await promises_default().mkdir(external_node_path_default().dirname(destPath), {
|
|
688
|
+
recursive: true
|
|
689
|
+
});
|
|
690
|
+
await promises_default().writeFile(destPath, buf);
|
|
691
|
+
return destPath;
|
|
692
|
+
};
|
|
693
|
+
async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store) {
|
|
694
|
+
const tmpDir = await createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
|
|
695
|
+
const uploaded = [];
|
|
696
|
+
try {
|
|
697
|
+
for (const item of uploadResults)try {
|
|
698
|
+
const sharedKey = normalizedKey(item.name, item.version);
|
|
699
|
+
const config = normalizedConfig[sharedKey];
|
|
700
|
+
if (!config) {
|
|
701
|
+
logger_logger.error(`No config found for ${item.name}`);
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
const { uploadOptions } = config;
|
|
705
|
+
if (!uploadOptions) throw new Error(`No uploadOptions found for ${item.name}`);
|
|
706
|
+
const filename = external_node_path_default().basename(new URL(item.cdnUrl, 'http://dummy.com').pathname);
|
|
707
|
+
const localPath = external_node_path_default().join(tmpDir, filename);
|
|
708
|
+
const hash = createCacheHash({
|
|
709
|
+
...config,
|
|
710
|
+
plugins: config.plugins || []
|
|
711
|
+
}, item.type);
|
|
712
|
+
const cdnUrl = publisher.publicUrl({
|
|
713
|
+
sharedName: item.name,
|
|
714
|
+
hash,
|
|
715
|
+
fileName: filename,
|
|
716
|
+
options: uploadOptions
|
|
717
|
+
});
|
|
718
|
+
const hitCache = await publisher.exists(cdnUrl);
|
|
719
|
+
if (hitCache) {
|
|
720
|
+
logger_logger.info(`Hit cache for ${item.name}@${item.version} -> ${cdnUrl}`);
|
|
721
|
+
uploaded.push({
|
|
722
|
+
...item,
|
|
723
|
+
cdnUrl
|
|
724
|
+
});
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
logger_logger.info(`Downloading ${item.name}@${item.version} -> ${localPath}`);
|
|
728
|
+
const t0 = Date.now();
|
|
729
|
+
const cdnPath = retrieveCDNPath({
|
|
730
|
+
config,
|
|
731
|
+
sharedKey,
|
|
732
|
+
type: item.type
|
|
733
|
+
});
|
|
734
|
+
await store.downloadFile(cdnPath, localPath);
|
|
735
|
+
const tDownload = Date.now() - t0;
|
|
736
|
+
logger_logger.info(`Downloaded ${item.name}@${item.version} in ${tDownload}ms`);
|
|
737
|
+
const newCdnUrl = await publisher.publishFile({
|
|
738
|
+
localPath,
|
|
739
|
+
sharedName: item.name,
|
|
740
|
+
hash,
|
|
741
|
+
options: uploadOptions
|
|
742
|
+
});
|
|
743
|
+
uploaded.push({
|
|
744
|
+
...item,
|
|
745
|
+
cdnUrl: newCdnUrl
|
|
746
|
+
});
|
|
747
|
+
} catch (error) {
|
|
748
|
+
logger_logger.error(`Failed to upload ${item.name}@${item.version}: ${error}`);
|
|
749
|
+
}
|
|
750
|
+
for (const s of sharedFilePaths)try {
|
|
751
|
+
const config = normalizedConfig[normalizedKey(s.name, s.version)];
|
|
752
|
+
if (!config) {
|
|
753
|
+
logger_logger.error(`No config found for ${s.name}`);
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
const { uploadOptions } = config;
|
|
757
|
+
if (!uploadOptions) throw new Error(`No uploadOptions found for ${s.name}`);
|
|
758
|
+
const hash = createCacheHash({
|
|
759
|
+
...config,
|
|
760
|
+
plugins: config.plugins || []
|
|
761
|
+
}, s.type);
|
|
762
|
+
const cdnUrl = await publisher.publishFile({
|
|
763
|
+
localPath: s.filepath,
|
|
764
|
+
sharedName: s.name,
|
|
765
|
+
hash,
|
|
766
|
+
options: uploadOptions
|
|
767
|
+
});
|
|
768
|
+
uploaded.push({
|
|
769
|
+
name: s.name,
|
|
770
|
+
version: s.version,
|
|
771
|
+
globalName: s.globalName,
|
|
772
|
+
cdnUrl,
|
|
773
|
+
type: s.type
|
|
774
|
+
});
|
|
775
|
+
} catch (error) {
|
|
776
|
+
logger_logger.error(`Failed to upload ${s.name}@${s.version}: ${error}`);
|
|
777
|
+
}
|
|
778
|
+
return uploaded;
|
|
779
|
+
} finally{
|
|
780
|
+
await promises_default().rm(tmpDir, {
|
|
781
|
+
recursive: true,
|
|
782
|
+
force: true
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOptions, store, publisher) {
|
|
787
|
+
const cacheUploaded = await uploadToCacheStore(sharedFilePaths, normalizedConfig, store);
|
|
788
|
+
if (!uploadOptions) {
|
|
789
|
+
const hydrated = await Promise.all(uploadResults.map(async (item)=>{
|
|
790
|
+
if ('full' !== item.type) return item;
|
|
791
|
+
const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
|
|
792
|
+
const jsonPath = external_node_path_default().join(tmpDir, `${item.name}-${item.version}.json`);
|
|
793
|
+
try {
|
|
794
|
+
const tJson0 = Date.now();
|
|
795
|
+
const sharedKey = normalizedKey(item.name, item.version);
|
|
796
|
+
const config = normalizedConfig[sharedKey];
|
|
797
|
+
if (config) {
|
|
798
|
+
const cdnPath = retrieveCDNPath({
|
|
799
|
+
config,
|
|
800
|
+
sharedKey,
|
|
801
|
+
type: item.type
|
|
802
|
+
});
|
|
803
|
+
const jsonCdnPath = cdnPath.replace(/\.js$/, '.json');
|
|
804
|
+
await store.downloadFile(jsonCdnPath, jsonPath);
|
|
805
|
+
} else await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
|
|
806
|
+
const tJson = Date.now() - tJson0;
|
|
807
|
+
logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
|
|
808
|
+
const jsonContent = JSON.parse(await promises_default().readFile(jsonPath, 'utf8'));
|
|
809
|
+
return {
|
|
810
|
+
...item,
|
|
811
|
+
canTreeShaking: jsonContent.canTreeShaking,
|
|
812
|
+
modules: jsonContent.modules
|
|
813
|
+
};
|
|
814
|
+
} catch (jsonError) {
|
|
815
|
+
logger_logger.error(`Failed to download ${item.name}@${item.version} json: ${jsonError}`);
|
|
816
|
+
return {
|
|
817
|
+
...item,
|
|
818
|
+
canTreeShaking: item.canTreeShaking ?? true
|
|
819
|
+
};
|
|
820
|
+
} finally{
|
|
821
|
+
await promises_default().rm(tmpDir, {
|
|
822
|
+
recursive: true,
|
|
823
|
+
force: true
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}));
|
|
827
|
+
return [
|
|
828
|
+
...cacheUploaded,
|
|
829
|
+
...hydrated
|
|
830
|
+
];
|
|
831
|
+
}
|
|
832
|
+
if (!publisher) throw new Error('uploadOptions provided but no projectPublisher configured (configure the selected adapter to enable it or omit uploadOptions)');
|
|
833
|
+
const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
|
|
834
|
+
return projectUploadResults;
|
|
835
|
+
}
|
|
836
|
+
const buildLimit = external_p_limit_default()(Math.max(1, external_node_os_default().cpus().length));
|
|
837
|
+
const buildRoute = new external_hono_namespaceObject.Hono();
|
|
838
|
+
buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', ConfigSchema), async (c)=>{
|
|
839
|
+
const logger = c.get('logger');
|
|
840
|
+
const startTime = Date.now();
|
|
841
|
+
const jobId = (0, external_nanoid_namespaceObject.nanoid)();
|
|
842
|
+
logger.info(jobId);
|
|
843
|
+
const body = c.req.valid('json');
|
|
844
|
+
logger.info(JSON.stringify(body));
|
|
845
|
+
const normalizedConfig = normalizeConfig(body);
|
|
846
|
+
const store = c.get('objectStore');
|
|
847
|
+
const publisher = c.get('projectPublisher');
|
|
848
|
+
try {
|
|
849
|
+
const t0 = Date.now();
|
|
850
|
+
const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
|
|
851
|
+
const tRetrieveCache = Date.now();
|
|
852
|
+
logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
|
|
853
|
+
let sharedFilePaths = [];
|
|
854
|
+
let dir;
|
|
855
|
+
if (Object.keys(restConfig).length > 0) {
|
|
856
|
+
const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
|
|
857
|
+
sharedFilePaths = buildResult.sharedFilePaths;
|
|
858
|
+
dir = buildResult.dir;
|
|
859
|
+
}
|
|
860
|
+
const tBuild = Date.now();
|
|
861
|
+
logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
|
|
862
|
+
const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
|
|
863
|
+
const tUpload = Date.now();
|
|
864
|
+
logger.info(`upload took ${tUpload - tBuild}ms`);
|
|
865
|
+
cleanUp(dir).catch((err)=>{
|
|
866
|
+
logger.error(`Failed to cleanup dir ${dir}: ${err}`);
|
|
867
|
+
});
|
|
868
|
+
const tCleanUp = Date.now();
|
|
869
|
+
logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
|
|
870
|
+
return c.json({
|
|
871
|
+
jobId,
|
|
872
|
+
status: 'success',
|
|
873
|
+
data: uploadResults,
|
|
874
|
+
cached: cacheItems,
|
|
875
|
+
duration: Date.now() - startTime
|
|
876
|
+
});
|
|
877
|
+
} catch (error) {
|
|
878
|
+
if (error instanceof CommandExecutionError) {
|
|
879
|
+
if (137 === error.exitCode) maybePrune();
|
|
880
|
+
return c.json({
|
|
881
|
+
jobId,
|
|
882
|
+
status: 'failed',
|
|
883
|
+
error: error.message,
|
|
884
|
+
command: error.command,
|
|
885
|
+
exitCode: error.exitCode,
|
|
886
|
+
stdout: error.stdout,
|
|
887
|
+
stderr: error.stderr
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
return c.json({
|
|
891
|
+
jobId,
|
|
892
|
+
status: 'failed',
|
|
893
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
async function handleCheckTreeshake(c, body) {
|
|
898
|
+
const logger = c.get('logger');
|
|
899
|
+
const startTime = Date.now();
|
|
900
|
+
const jobId = (0, external_nanoid_namespaceObject.nanoid)();
|
|
901
|
+
logger.info(jobId);
|
|
902
|
+
logger.info(JSON.stringify(body));
|
|
903
|
+
const normalizedConfig = normalizeConfig(body);
|
|
904
|
+
const store = c.get('objectStore');
|
|
905
|
+
const publisher = c.get('projectPublisher');
|
|
906
|
+
try {
|
|
907
|
+
const t0 = Date.now();
|
|
908
|
+
const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
|
|
909
|
+
const tRetrieveCache = Date.now();
|
|
910
|
+
logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
|
|
911
|
+
let sharedFilePaths = [];
|
|
912
|
+
let dir;
|
|
913
|
+
if (Object.keys(restConfig).length > 0) {
|
|
914
|
+
const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
|
|
915
|
+
sharedFilePaths = buildResult.sharedFilePaths;
|
|
916
|
+
dir = buildResult.dir;
|
|
917
|
+
}
|
|
918
|
+
const tBuild = Date.now();
|
|
919
|
+
logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
|
|
920
|
+
const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
|
|
921
|
+
const tUpload = Date.now();
|
|
922
|
+
logger.info(`upload took ${tUpload - tBuild}ms`);
|
|
923
|
+
cleanUp(dir).catch((err)=>{
|
|
924
|
+
logger.error(`Failed to cleanup dir ${dir}: ${err}`);
|
|
925
|
+
});
|
|
926
|
+
const tCleanUp = Date.now();
|
|
927
|
+
logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
|
|
928
|
+
return c.json({
|
|
929
|
+
jobId,
|
|
930
|
+
status: 'success',
|
|
931
|
+
data: uploadResults,
|
|
932
|
+
cached: cacheItems,
|
|
933
|
+
duration: Date.now() - startTime
|
|
934
|
+
});
|
|
935
|
+
} catch (error) {
|
|
936
|
+
if (error instanceof CommandExecutionError) {
|
|
937
|
+
if (137 === error.exitCode) maybePrune();
|
|
938
|
+
return c.json({
|
|
939
|
+
jobId,
|
|
940
|
+
status: 'failed',
|
|
941
|
+
error: error.message,
|
|
942
|
+
command: error.command,
|
|
943
|
+
exitCode: error.exitCode,
|
|
944
|
+
stdout: error.stdout,
|
|
945
|
+
stderr: error.stderr
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
return c.json({
|
|
949
|
+
jobId,
|
|
950
|
+
status: 'failed',
|
|
951
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
buildRoute.post('/check-tree-shaking', (0, zod_validator_namespaceObject.zValidator)('json', CheckTreeShakingSchema), async (c)=>handleCheckTreeshake(c, c.req.valid('json')));
|
|
956
|
+
const maintenanceRoute = new external_hono_namespaceObject.Hono();
|
|
957
|
+
maintenanceRoute.post('/', async (c)=>{
|
|
958
|
+
const logger = c.get('logger');
|
|
959
|
+
const jobId = (0, external_nanoid_namespaceObject.nanoid)();
|
|
960
|
+
const startTime = Date.now();
|
|
961
|
+
logger.info(jobId);
|
|
962
|
+
await maybePrune();
|
|
963
|
+
return c.json({
|
|
964
|
+
jobId,
|
|
965
|
+
status: 'success',
|
|
966
|
+
duration: Date.now() - startTime
|
|
967
|
+
});
|
|
968
|
+
});
|
|
969
|
+
const routes = new external_hono_namespaceObject.Hono();
|
|
970
|
+
routes.route('/build', buildRoute);
|
|
971
|
+
routes.route('/clean-cache', maintenanceRoute);
|
|
972
|
+
function createApp(deps, opts) {
|
|
973
|
+
var _opts_appExtensions, _opts_frontendAdapters;
|
|
974
|
+
if (null == opts ? void 0 : opts.logger) setLogger(opts.logger);
|
|
975
|
+
setRuntimeEnv((null == opts ? void 0 : opts.runtimeEnv) ?? process.env);
|
|
976
|
+
const app = new external_hono_namespaceObject.Hono();
|
|
977
|
+
const corsOrigin = (null == opts ? void 0 : opts.corsOrigin) ?? '*';
|
|
978
|
+
const staticRootDir = (null == opts ? void 0 : opts.staticRootDir) ?? DEFAULT_STATIC_ROOT;
|
|
979
|
+
app.use('*', (0, cors_namespaceObject.cors)({
|
|
980
|
+
origin: corsOrigin,
|
|
981
|
+
allowMethods: [
|
|
982
|
+
'GET',
|
|
983
|
+
'POST',
|
|
984
|
+
'OPTIONS'
|
|
985
|
+
],
|
|
986
|
+
allowHeaders: [
|
|
987
|
+
'Content-Type',
|
|
988
|
+
'Authorization'
|
|
989
|
+
]
|
|
990
|
+
}));
|
|
991
|
+
app.use('*', loggerMiddleware);
|
|
992
|
+
app.use('*', createDiMiddleware(deps));
|
|
993
|
+
if (null == opts ? void 0 : null == (_opts_appExtensions = opts.appExtensions) ? void 0 : _opts_appExtensions.length) for (const extend of opts.appExtensions)extend(app);
|
|
994
|
+
app.get('/tree-shaking-shared/healthz', (c)=>c.json({
|
|
995
|
+
status: 'ok',
|
|
996
|
+
timestamp: new Date().toISOString()
|
|
997
|
+
}));
|
|
998
|
+
app.route('/tree-shaking-shared', routes);
|
|
999
|
+
app.route('/', createStaticRoute({
|
|
1000
|
+
rootDir: staticRootDir
|
|
1001
|
+
}));
|
|
1002
|
+
if (null == opts ? void 0 : null == (_opts_frontendAdapters = opts.frontendAdapters) ? void 0 : _opts_frontendAdapters.length) for (const adapter of opts.frontendAdapters)adapter.register(app);
|
|
1003
|
+
startPeriodicPrune(null == opts ? void 0 : opts.pruneIntervalMs);
|
|
1004
|
+
return app;
|
|
1005
|
+
}
|
|
1006
|
+
const defaultBasePath = '/tree-shaking';
|
|
1007
|
+
const embeddedAdapter_contentTypeByExt = (filePath)=>{
|
|
1008
|
+
if (filePath.endsWith('.html')) return 'text/html; charset=utf-8';
|
|
1009
|
+
if (filePath.endsWith('.js')) return "application/javascript";
|
|
1010
|
+
if (filePath.endsWith('.css')) return 'text/css';
|
|
1011
|
+
if (filePath.endsWith('.json')) return 'application/json';
|
|
1012
|
+
if (filePath.endsWith('.svg')) return 'image/svg+xml';
|
|
1013
|
+
if (filePath.endsWith('.png')) return 'image/png';
|
|
1014
|
+
if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) return 'image/jpeg';
|
|
1015
|
+
if (filePath.endsWith('.webp')) return 'image/webp';
|
|
1016
|
+
if (filePath.endsWith('.ico')) return 'image/x-icon';
|
|
1017
|
+
return 'application/octet-stream';
|
|
1018
|
+
};
|
|
1019
|
+
const safeResolve = (rootDir, requestPath)=>{
|
|
1020
|
+
const rootResolved = external_node_path_default().resolve(rootDir);
|
|
1021
|
+
const rel = requestPath.replace(/^\/+/, '');
|
|
1022
|
+
const filePath = external_node_path_default().resolve(rootResolved, rel);
|
|
1023
|
+
if (filePath !== rootResolved && !filePath.startsWith(`${rootResolved}${external_node_path_default().sep}`)) return null;
|
|
1024
|
+
return filePath;
|
|
1025
|
+
};
|
|
1026
|
+
function createEmbeddedFrontendAdapter(opts) {
|
|
1027
|
+
const basePath = opts.basePath ?? defaultBasePath;
|
|
1028
|
+
const normalizedBase = '/' === basePath ? '' : basePath.replace(/\/$/, '');
|
|
1029
|
+
const distDir = opts.distDir;
|
|
1030
|
+
const indexFile = opts.indexFile ?? 'index.html';
|
|
1031
|
+
const spaFallback = opts.spaFallback ?? true;
|
|
1032
|
+
const handler = async (c)=>{
|
|
1033
|
+
let requestPath = c.req.path;
|
|
1034
|
+
try {
|
|
1035
|
+
requestPath = decodeURIComponent(requestPath);
|
|
1036
|
+
} catch {
|
|
1037
|
+
return c.text('Not Found', 404);
|
|
1038
|
+
}
|
|
1039
|
+
let relPath = requestPath;
|
|
1040
|
+
if (normalizedBase && requestPath.startsWith(normalizedBase)) relPath = requestPath.slice(normalizedBase.length);
|
|
1041
|
+
if (!relPath || '/' === relPath) relPath = `/${indexFile}`;
|
|
1042
|
+
const filePath = safeResolve(distDir, relPath);
|
|
1043
|
+
if (filePath) try {
|
|
1044
|
+
const stat = await external_node_fs_default().promises.stat(filePath);
|
|
1045
|
+
if (stat.isFile()) {
|
|
1046
|
+
const buf = await external_node_fs_default().promises.readFile(filePath);
|
|
1047
|
+
return new Response(buf, {
|
|
1048
|
+
status: 200,
|
|
1049
|
+
headers: {
|
|
1050
|
+
'Content-Type': embeddedAdapter_contentTypeByExt(filePath)
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
} catch {}
|
|
1055
|
+
if (!spaFallback) return c.text('Not Found', 404);
|
|
1056
|
+
const fallbackPath = safeResolve(distDir, `/${indexFile}`);
|
|
1057
|
+
if (!fallbackPath) return c.text('Not Found', 404);
|
|
1058
|
+
try {
|
|
1059
|
+
const buf = await external_node_fs_default().promises.readFile(fallbackPath);
|
|
1060
|
+
return new Response(buf, {
|
|
1061
|
+
status: 200,
|
|
1062
|
+
headers: {
|
|
1063
|
+
'Content-Type': embeddedAdapter_contentTypeByExt(fallbackPath)
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
} catch {
|
|
1067
|
+
return c.text('Not Found', 404);
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
return {
|
|
1071
|
+
id: 'treeshake-embedded-frontend',
|
|
1072
|
+
register (app) {
|
|
1073
|
+
const base = normalizedBase || '/';
|
|
1074
|
+
app.get(base, handler);
|
|
1075
|
+
app.get(`${base}/*`, handler);
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
const node_server_namespaceObject = require("@hono/node-server");
|
|
1080
|
+
function createServer(opts) {
|
|
1081
|
+
const port = opts.port ?? 3000;
|
|
1082
|
+
const hostname = opts.hostname ?? '0.0.0.0';
|
|
1083
|
+
return (0, node_server_namespaceObject.serve)({
|
|
1084
|
+
fetch: opts.app.fetch,
|
|
1085
|
+
port,
|
|
1086
|
+
hostname
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
const hasIndexHtml = (dir)=>Boolean(dir && external_node_fs_default().existsSync(external_node_path_default().join(dir, 'index.html')));
|
|
1090
|
+
const resolveFrontendDistDir = ()=>{
|
|
1091
|
+
const envDir = process.env.TREESHAKE_FRONTEND_DIST;
|
|
1092
|
+
if (envDir) {
|
|
1093
|
+
if (hasIndexHtml(envDir)) return envDir;
|
|
1094
|
+
throw new Error(`TREESHAKE_FRONTEND_DIST is set but index.html is missing: ${envDir}`);
|
|
1095
|
+
}
|
|
1096
|
+
const candidates = [
|
|
1097
|
+
external_node_path_default().resolve(__dirname, 'frontend'),
|
|
1098
|
+
external_node_path_default().resolve(__dirname, '..', 'frontend')
|
|
1099
|
+
];
|
|
1100
|
+
for (const candidate of candidates)if (hasIndexHtml(candidate)) return candidate;
|
|
1101
|
+
const workspaceCandidates = [
|
|
1102
|
+
external_node_path_default().resolve(process.cwd(), 'packages', 'treeshake-frontend', 'dist'),
|
|
1103
|
+
external_node_path_default().resolve(process.cwd(), 'treeshake-frontend', 'dist')
|
|
1104
|
+
];
|
|
1105
|
+
for (const candidate of workspaceCandidates)if (hasIndexHtml(candidate)) return candidate;
|
|
1106
|
+
};
|
|
1107
|
+
async function main() {
|
|
1108
|
+
const registry = createOssAdapterRegistry();
|
|
1109
|
+
const resolved = resolveOssEnv({
|
|
1110
|
+
env: {
|
|
1111
|
+
...process.env,
|
|
1112
|
+
ADAPTER_ID: 'local'
|
|
1113
|
+
},
|
|
1114
|
+
registry,
|
|
1115
|
+
defaultAdapterId: 'local'
|
|
1116
|
+
});
|
|
1117
|
+
const logger = createLogger({
|
|
1118
|
+
level: resolved.logLevel
|
|
1119
|
+
});
|
|
1120
|
+
const deps = await createAdapterDeps({
|
|
1121
|
+
registry,
|
|
1122
|
+
adapterId: resolved.adapterId,
|
|
1123
|
+
adapterConfig: resolved.adapterConfig,
|
|
1124
|
+
logger
|
|
1125
|
+
});
|
|
1126
|
+
const frontendAdapters = [];
|
|
1127
|
+
const distDir = resolveFrontendDistDir();
|
|
1128
|
+
if (!distDir) throw new Error('Treeshake UI dist not found. Rebuild the CLI bundle or set TREESHAKE_FRONTEND_DIST.');
|
|
1129
|
+
frontendAdapters.push(createEmbeddedFrontendAdapter({
|
|
1130
|
+
basePath: process.env.TREESHAKE_FRONTEND_BASE_PATH ?? '/tree-shaking',
|
|
1131
|
+
distDir,
|
|
1132
|
+
spaFallback: '0' !== process.env.TREESHAKE_FRONTEND_SPA_FALLBACK && 'false' !== process.env.TREESHAKE_FRONTEND_SPA_FALLBACK
|
|
1133
|
+
}));
|
|
1134
|
+
const app = createApp(deps, {
|
|
1135
|
+
corsOrigin: resolved.corsOrigin,
|
|
1136
|
+
staticRootDir: resolved.staticRootDir,
|
|
1137
|
+
pruneIntervalMs: resolved.pruneIntervalMs,
|
|
1138
|
+
logger,
|
|
1139
|
+
runtimeEnv: resolved.runtimeEnv,
|
|
1140
|
+
frontendAdapters
|
|
1141
|
+
});
|
|
1142
|
+
createServer({
|
|
1143
|
+
app,
|
|
1144
|
+
port: resolved.port,
|
|
1145
|
+
hostname: resolved.hostname
|
|
1146
|
+
});
|
|
1147
|
+
console.log(`Build service listening on http://${resolved.hostname}:${resolved.port}`);
|
|
1148
|
+
if (frontendAdapters.length) {
|
|
1149
|
+
const basePath = process.env.TREESHAKE_FRONTEND_BASE_PATH ?? '/tree-shaking';
|
|
1150
|
+
console.log(`Treeshake UI available at http://${resolved.hostname}:${resolved.port}${basePath}`);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
main().catch((err)=>{
|
|
1154
|
+
console.error(err);
|
|
1155
|
+
process.exit(1);
|
|
1156
|
+
});
|
|
1157
|
+
for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
1158
|
+
Object.defineProperty(exports, '__esModule', {
|
|
1159
|
+
value: true
|
|
1160
|
+
});
|