@arcote.tech/arc-cli 0.4.6 → 0.4.8

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.
@@ -7,12 +7,13 @@ import {
7
7
  discoverPackages,
8
8
  isContextPackage,
9
9
  type BuildManifest,
10
+ type ModuleEntry,
10
11
  type WorkspacePackage,
11
12
  } from "../builder/module-builder";
12
13
 
13
14
  // Re-export for convenience
14
15
  export { buildPackages, buildStyles, isContextPackage };
15
- export type { BuildManifest, WorkspacePackage };
16
+ export type { BuildManifest, ModuleEntry, WorkspacePackage };
16
17
 
17
18
  // ---------------------------------------------------------------------------
18
19
  // Logging
@@ -134,7 +135,7 @@ export async function buildAll(ws: WorkspaceInfo): Promise<BuildManifest> {
134
135
  }
135
136
 
136
137
  log("Building shell...");
137
- await buildShell(ws.shellDir);
138
+ await buildShell(ws.shellDir, ws.packages);
138
139
  ok("Shell built");
139
140
 
140
141
  return manifest;
@@ -144,7 +145,30 @@ export async function buildAll(ws: WorkspaceInfo): Promise<BuildManifest> {
144
145
  // Shell builder — framework packages for import map
145
146
  // ---------------------------------------------------------------------------
146
147
 
147
- async function buildShell(outDir: string): Promise<void> {
148
+ /** Collect all @arcote.tech/* peerDependencies from workspace packages. */
149
+ export function collectArcPeerDeps(packages: WorkspacePackage[]): [string, string][] {
150
+ const seen = new Set<string>();
151
+ // Always include core framework packages
152
+ for (const pkg of ["@arcote.tech/arc", "@arcote.tech/arc-ds", "@arcote.tech/arc-react", "@arcote.tech/platform"]) {
153
+ seen.add(pkg);
154
+ }
155
+ // Scan all workspace packages for @arcote.tech/* peerDeps
156
+ for (const wp of packages) {
157
+ const peerDeps = wp.packageJson.peerDependencies ?? {};
158
+ for (const dep of Object.keys(peerDeps)) {
159
+ if (dep.startsWith("@arcote.tech/")) seen.add(dep);
160
+ }
161
+ }
162
+ // Convert to [shortName, fullName] entries
163
+ return [...seen].map((pkg) => {
164
+ const short = pkg === "@arcote.tech/platform"
165
+ ? "platform"
166
+ : pkg.replace("@arcote.tech/", "");
167
+ return [short, pkg];
168
+ });
169
+ }
170
+
171
+ async function buildShell(outDir: string, packages?: WorkspacePackage[]): Promise<void> {
148
172
  mkdirSync(outDir, { recursive: true });
149
173
  const tmpDir = join(outDir, "_tmp");
150
174
  mkdirSync(tmpDir, { recursive: true });
@@ -202,46 +226,53 @@ export const { createPortal, flushSync } = ReactDOM;`,
202
226
  }
203
227
 
204
228
  // Step 2: Build Arc layer (react is EXTERNAL — resolved via import map)
205
- const arcEntries: [string, string][] = [
206
- ["arc", "@arcote.tech/arc"],
207
- ["arc-ds", "@arcote.tech/arc-ds"],
208
- ["arc-react", "@arcote.tech/arc-react"],
209
- ["arc-auth", "@arcote.tech/arc-auth"],
210
- ["arc-utils", "@arcote.tech/arc-utils"],
211
- ["arc-workspace", "@arcote.tech/arc-workspace"],
212
- ["platform", "@arcote.tech/platform"],
229
+ // Dynamically collect only @arcote.tech/* packages actually used by workspace
230
+ const arcEntries = packages
231
+ ? collectArcPeerDeps(packages)
232
+ : [
233
+ // Fallback: core packages only (no workspace packages available)
234
+ ["arc", "@arcote.tech/arc"],
235
+ ["arc-ds", "@arcote.tech/arc-ds"],
236
+ ["arc-react", "@arcote.tech/arc-react"],
237
+ ["platform", "@arcote.tech/platform"],
238
+ ] as [string, string][];
239
+
240
+ const baseExternal = [
241
+ "react",
242
+ "react-dom",
243
+ "react/jsx-runtime",
244
+ "react/jsx-dev-runtime",
245
+ "react-dom/client",
213
246
  ];
247
+ const allArcPkgs = arcEntries.map(([, pkg]) => pkg);
214
248
 
215
- const arcEps: string[] = [];
249
+ // Build each arc entry separately so it can import sibling arc packages
250
+ // as externals (resolved via import map) without circular self-reference.
216
251
  for (const [name, pkg] of arcEntries) {
217
252
  const f = join(tmpDir, `${name}.ts`);
218
253
  Bun.write(f, `export * from "${pkg}";\n`);
219
- arcEps.push(f);
220
- }
221
254
 
222
- const r2 = await Bun.build({
223
- entrypoints: arcEps,
224
- outdir: outDir,
225
- splitting: true,
226
- format: "esm",
227
- target: "browser",
228
- naming: "[name].[ext]",
229
- external: [
230
- "react",
231
- "react-dom",
232
- "react/jsx-runtime",
233
- "react/jsx-dev-runtime",
234
- "react-dom/client",
235
- ],
236
- define: {
237
- ONLY_SERVER: "false",
238
- ONLY_BROWSER: "true",
239
- ONLY_CLIENT: "true",
240
- },
241
- });
242
- if (!r2.success) {
243
- for (const l of r2.logs) console.error(l);
244
- throw new Error("Shell Arc build failed");
255
+ const r2 = await Bun.build({
256
+ entrypoints: [f],
257
+ outdir: outDir,
258
+ format: "esm",
259
+ target: "browser",
260
+ naming: "[name].[ext]",
261
+ external: [
262
+ ...baseExternal,
263
+ // Other arc packages are external (not self)
264
+ ...allArcPkgs.filter((p) => p !== pkg),
265
+ ],
266
+ define: {
267
+ ONLY_SERVER: "false",
268
+ ONLY_BROWSER: "true",
269
+ ONLY_CLIENT: "true",
270
+ },
271
+ });
272
+ if (!r2.success) {
273
+ for (const l of r2.logs) console.error(l);
274
+ throw new Error(`Shell build failed for ${pkg}`);
275
+ }
245
276
  }
246
277
 
247
278
  // Clean tmp
@@ -255,9 +286,9 @@ export const { createPortal, flushSync } = ReactDOM;`,
255
286
 
256
287
  export async function loadServerContext(
257
288
  packages: WorkspacePackage[],
258
- ): Promise<any | null> {
289
+ ): Promise<{ context: any | null; moduleAccess: Map<string, any> }> {
259
290
  const ctxPackages = packages.filter((p) => isContextPackage(p.packageJson));
260
- if (ctxPackages.length === 0) return null;
291
+ if (ctxPackages.length === 0) return { context: null, moduleAccess: new Map() };
261
292
 
262
293
  // Set globals for server context — framework packages (arc-auth etc.)
263
294
  // use these at runtime to tree-shake browser/server code paths.
@@ -278,6 +309,7 @@ export async function loadServerContext(
278
309
  // Pre-import platform so it's cached with this absolute path
279
310
  await import(platformEntry);
280
311
 
312
+ // Import context packages from server dist (has server-only code paths)
281
313
  for (const ctx of ctxPackages) {
282
314
  const serverDist = join(ctx.path, "dist", "server", "main", "index.js");
283
315
  if (!existsSync(serverDist)) {
@@ -292,6 +324,20 @@ export async function loadServerContext(
292
324
  }
293
325
  }
294
326
 
295
- const { getContext } = await import(platformEntry);
296
- return getContext() ?? null;
327
+ // Import non-context packages from source to capture module().protectedBy() metadata
328
+ const nonCtxPackages = packages.filter((p) => !isContextPackage(p.packageJson));
329
+ for (const pkg of nonCtxPackages) {
330
+ try {
331
+ await import(pkg.entrypoint);
332
+ } catch {
333
+ // Non-context packages may fail on server (React components etc.) — that's OK,
334
+ // module().protectedBy().build() runs synchronously before any rendering
335
+ }
336
+ }
337
+
338
+ const { getContext, getAllModuleAccess } = await import(platformEntry);
339
+ return {
340
+ context: getContext() ?? null,
341
+ moduleAccess: getAllModuleAccess(),
342
+ };
297
343
  }