@arcote.tech/arc-cli 0.3.0 → 0.3.1

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/dist/index.js CHANGED
@@ -11202,7 +11202,7 @@ import { dirname as dirname2 } from "path";
11202
11202
 
11203
11203
  // src/utils/build.ts
11204
11204
  import { spawn } from "child_process";
11205
- import { readFileSync as readFileSync2 } from "fs";
11205
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
11206
11206
  import { dirname, join as join2 } from "path";
11207
11207
 
11208
11208
  // src/utils/config.ts
@@ -16541,27 +16541,17 @@ function getContextsFromConfig(config, configDir) {
16541
16541
  }
16542
16542
  function generateBaseTypes(config, configDir) {
16543
16543
  const { clients } = config;
16544
- const arcDir = join(configDir, ".arc");
16545
- if (!existsSync(arcDir)) {
16546
- mkdirSync(arcDir, { recursive: true });
16547
- }
16548
- let typesDefs = `declare global {
16549
- `;
16544
+ let typesDefs = "";
16550
16545
  clients.forEach((client) => {
16551
16546
  const normalizedClient = normalizeClientName(client);
16552
- typesDefs += ` const ${normalizedClient}: boolean;
16547
+ typesDefs += `declare const ${normalizedClient}: boolean;
16553
16548
  `;
16554
- typesDefs += ` const NOT_ON_${normalizedClient}: boolean;
16549
+ typesDefs += `declare const NOT_ON_${normalizedClient}: boolean;
16555
16550
  `;
16556
- typesDefs += ` const ONLY_${normalizedClient}: boolean;
16551
+ typesDefs += `declare const ONLY_${normalizedClient}: boolean;
16557
16552
  `;
16558
16553
  });
16559
- typesDefs += `}
16560
-
16561
- export {};
16562
- `;
16563
- writeFileSync(join(arcDir, "types.ts"), typesDefs);
16564
- writeFileSync(join(configDir, "arc.types.ts"), typesDefs);
16554
+ writeFileSync(join(configDir, "arc.d.ts"), typesDefs);
16565
16555
  }
16566
16556
  function generateClientTypes(config, configDir, client) {
16567
16557
  const { clients } = config;
@@ -16570,23 +16560,18 @@ function generateClientTypes(config, configDir, client) {
16570
16560
  if (!existsSync(clientTypesDir)) {
16571
16561
  mkdirSync(clientTypesDir, { recursive: true });
16572
16562
  }
16573
- let typesDefs = `declare global {
16574
- `;
16563
+ let typesDefs = "";
16575
16564
  clients.forEach((c) => {
16576
16565
  const normalizedC = normalizeClientName(c);
16577
16566
  const isCurrentClient = normalizeClientName(client) === normalizedC;
16578
- typesDefs += ` const ${normalizedC}: ${isCurrentClient ? "true" : "false"};
16567
+ typesDefs += `declare const ${normalizedC}: ${isCurrentClient ? "true" : "false"};
16579
16568
  `;
16580
- typesDefs += ` const NOT_ON_${normalizedC}: ${isCurrentClient ? "false" : "true"};
16569
+ typesDefs += `declare const NOT_ON_${normalizedC}: ${isCurrentClient ? "false" : "true"};
16581
16570
  `;
16582
- typesDefs += ` const ONLY_${normalizedC}: ${isCurrentClient ? "true" : "false"};
16571
+ typesDefs += `declare const ONLY_${normalizedC}: ${isCurrentClient ? "true" : "false"};
16583
16572
  `;
16584
16573
  });
16585
- typesDefs += `}
16586
-
16587
- export {};
16588
- `;
16589
- const typesPath = join(clientTypesDir, `${normalizeClientName(client).toLowerCase()}.ts`);
16574
+ const typesPath = join(clientTypesDir, `${normalizeClientName(client).toLowerCase()}.d.ts`);
16590
16575
  writeFileSync(typesPath, typesDefs);
16591
16576
  return typesPath;
16592
16577
  }
@@ -16657,7 +16642,7 @@ async function buildContextBundle(configPath, config, context, client, watch = f
16657
16642
  const externals = getExternals(config, configDir, client);
16658
16643
  const externalArgs = externals.map((dep) => `--external=${dep}`).join(" ");
16659
16644
  const buildTarget = client === "browser" ? "browser" : "bun";
16660
- const outDirPath = join2(configDir, config.outDir, context.name, client.toLowerCase());
16645
+ const outDirPath = join2(configDir, config.outDir, client.toLowerCase(), context.name);
16661
16646
  const command = `bun build ${context.fullPath} --target=${buildTarget} --outdir=${outDirPath} ${defineArgs} ${externalArgs}${watch ? " --watch" : ""}`;
16662
16647
  return new Promise((resolve, reject) => {
16663
16648
  const proc2 = spawn(command, { shell: true, cwd: configDir });
@@ -16670,27 +16655,29 @@ async function buildContextBundle(configPath, config, context, client, watch = f
16670
16655
  errorOutput += data.toString();
16671
16656
  });
16672
16657
  proc2.on("close", (code) => {
16673
- if (code !== 0 && !watch) {
16658
+ if (code !== 0 && !watch)
16674
16659
  reject(new Error(`Build failed: ${errorOutput || output}`));
16675
- } else if (!watch) {
16660
+ else if (!watch)
16676
16661
  resolve();
16677
- }
16678
16662
  });
16679
16663
  });
16680
16664
  }
16681
16665
  async function buildContextDeclarations(configPath, config, context, client) {
16682
16666
  const configDir = dirname(configPath);
16683
16667
  generateClientTypes(config, configDir, client);
16684
- const outDir = join2(configDir, config.outDir, context.name, client.toLowerCase());
16685
- const tsgoResult = await runTsgo(context.fullPath, outDir, configDir);
16686
- if (tsgoResult !== null) {
16687
- return tsgoResult;
16688
- }
16689
- return runTsc(context.fullPath, outDir, configDir);
16668
+ const outDir = join2(configDir, config.outDir, client.toLowerCase());
16669
+ const arcTypesPath = join2(configDir, "arc.d.ts");
16670
+ const files = existsSync2(arcTypesPath) ? [context.fullPath, arcTypesPath] : [context.fullPath];
16671
+ let result = await runTsgo(files, outDir, configDir);
16672
+ if (result === null) {
16673
+ result = await runTsc(files, outDir, configDir);
16674
+ }
16675
+ return result;
16690
16676
  }
16691
- async function runTsgo(entryFile, outDir, cwd) {
16677
+ async function runTsgo(files, outDir, cwd) {
16678
+ const fileArgs = files.map((f) => `"${f}"`).join(" ");
16679
+ const command = `npx tsgo ${fileArgs} --declaration --emitDeclarationOnly --outDir "${outDir}" --rootDir "${cwd}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext --ignoreConfig`;
16692
16680
  return new Promise((resolve) => {
16693
- const command = `npx tsgo --declaration --emitDeclarationOnly --outDir "${outDir}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext --ignoreConfig "${entryFile}"`;
16694
16681
  const proc2 = spawn(command, { shell: true, cwd });
16695
16682
  let output = "";
16696
16683
  let errorOutput = "";
@@ -16700,26 +16687,21 @@ async function runTsgo(entryFile, outDir, cwd) {
16700
16687
  proc2.stderr.on("data", (data) => {
16701
16688
  errorOutput += data.toString();
16702
16689
  });
16703
- proc2.on("error", () => {
16704
- resolve(null);
16705
- });
16690
+ proc2.on("error", () => resolve(null));
16706
16691
  proc2.on("close", (code) => {
16707
16692
  if (errorOutput.includes("not found") || errorOutput.includes("ENOENT") || errorOutput.includes("Cannot find")) {
16708
16693
  resolve(null);
16709
16694
  return;
16710
16695
  }
16711
- const allOutput = output + errorOutput;
16712
- const errors = parseTypeScriptErrors(allOutput, cwd);
16713
- resolve({
16714
- success: code === 0 && errors.length === 0,
16715
- errors
16716
- });
16696
+ const errors = parseTypeScriptErrors(output + errorOutput, cwd);
16697
+ resolve({ success: code === 0 && errors.length === 0, errors });
16717
16698
  });
16718
16699
  });
16719
16700
  }
16720
- async function runTsc(entryFile, outDir, cwd) {
16701
+ async function runTsc(files, outDir, cwd) {
16702
+ const fileArgs = files.map((f) => `"${f}"`).join(" ");
16703
+ const command = `npx tsc ${fileArgs} --declaration --emitDeclarationOnly --outDir "${outDir}" --rootDir "${cwd}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext`;
16721
16704
  return new Promise((resolve) => {
16722
- const command = `npx tsc --declaration --emitDeclarationOnly --outDir "${outDir}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext "${entryFile}"`;
16723
16705
  const proc2 = spawn(command, { shell: true, cwd });
16724
16706
  let output = "";
16725
16707
  let errorOutput = "";
@@ -16730,27 +16712,21 @@ async function runTsc(entryFile, outDir, cwd) {
16730
16712
  errorOutput += data.toString();
16731
16713
  });
16732
16714
  proc2.on("close", (code) => {
16733
- const allOutput = output + errorOutput;
16734
- const errors = parseTypeScriptErrors(allOutput, cwd);
16735
- resolve({
16736
- success: code === 0 && errors.length === 0,
16737
- errors
16738
- });
16715
+ const errors = parseTypeScriptErrors(output + errorOutput, cwd);
16716
+ resolve({ success: code === 0 && errors.length === 0, errors });
16739
16717
  });
16740
16718
  });
16741
16719
  }
16742
16720
  function parseTypeScriptErrors(output, cwd) {
16743
16721
  const errors = [];
16744
- const lines = output.split(`
16745
- `);
16746
- for (const line of lines) {
16722
+ for (const line of output.split(`
16723
+ `)) {
16747
16724
  const match2 = line.match(/^(.+?)(?:\((\d+),(\d+)\)|:(\d+):(\d+))\s*[-:]\s*error\s+TS\d+:\s*(.+)$/);
16748
16725
  if (match2) {
16749
16726
  const file = match2[1].replace(cwd + "/", "");
16750
16727
  const lineNum = match2[2] || match2[4];
16751
16728
  const col = match2[3] || match2[5];
16752
- const message = match2[6];
16753
- errors.push(`${file}:${lineNum}:${col} - ${message}`);
16729
+ errors.push(`${file}:${lineNum}:${col} - ${match2[6]}`);
16754
16730
  } else if (line.includes("error TS")) {
16755
16731
  errors.push(line.trim());
16756
16732
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI tool for Arc framework",
5
5
  "module": "index.ts",
6
6
  "main": "dist/index.js",
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "child_process";
2
- import { readFileSync } from "fs";
2
+ import { existsSync, readFileSync } from "fs";
3
3
  import { dirname, join } from "path";
4
4
  import type { ArcConfig, ContextInfo } from "./config";
5
5
  import { generateClientTypes } from "./config";
@@ -23,9 +23,6 @@ export interface BuildTask {
23
23
  watch: boolean;
24
24
  }
25
25
 
26
- /**
27
- * Read package.json and extract peer dependencies
28
- */
29
26
  function getPeerDependencies(configDir: string): string[] {
30
27
  try {
31
28
  const packageJsonPath = join(configDir, "package.json");
@@ -36,9 +33,6 @@ function getPeerDependencies(configDir: string): string[] {
36
33
  }
37
34
  }
38
35
 
39
- /**
40
- * Get external dependencies for a specific client
41
- */
42
36
  function getExternals(
43
37
  config: ArcConfig,
44
38
  configDir: string,
@@ -46,27 +40,20 @@ function getExternals(
46
40
  ): string[] {
47
41
  const peerDeps = getPeerDependencies(configDir);
48
42
  const externals = new Set<string>(peerDeps);
49
-
50
43
  if (config.externals?.common) {
51
44
  config.externals.common.forEach((ext) => externals.add(ext));
52
45
  }
53
-
54
46
  const clientExternals =
55
47
  config.externals?.[client as keyof typeof config.externals];
56
48
  if (Array.isArray(clientExternals)) {
57
49
  clientExternals.forEach((ext) => externals.add(ext));
58
50
  }
59
-
60
51
  return Array.from(externals);
61
52
  }
62
53
 
63
- /**
64
- * Build a single context for a specific client
65
- */
66
54
  export async function buildContext(task: BuildTask): Promise<BuildResult> {
67
55
  const startTime = Date.now();
68
56
  const { configPath, config, context, client, watch } = task;
69
-
70
57
  const result: BuildResult = {
71
58
  success: false,
72
59
  context: context.name,
@@ -77,11 +64,8 @@ export async function buildContext(task: BuildTask): Promise<BuildResult> {
77
64
  };
78
65
 
79
66
  try {
80
- // Build the JavaScript bundle
81
67
  await buildContextBundle(configPath, config, context, client, watch);
82
68
  result.jsBuilt = true;
83
-
84
- // Build declarations
85
69
  if (!watch) {
86
70
  const declResult = await buildContextDeclarations(
87
71
  configPath,
@@ -92,7 +76,6 @@ export async function buildContext(task: BuildTask): Promise<BuildResult> {
92
76
  result.declarationsBuilt = declResult.success;
93
77
  result.declarationErrors = declResult.errors;
94
78
  }
95
-
96
79
  result.success = result.jsBuilt;
97
80
  result.duration = Date.now() - startTime;
98
81
  return result;
@@ -103,9 +86,6 @@ export async function buildContext(task: BuildTask): Promise<BuildResult> {
103
86
  }
104
87
  }
105
88
 
106
- /**
107
- * Build JavaScript bundle for a context
108
- */
109
89
  export async function buildContextBundle(
110
90
  configPath: string,
111
91
  config: ArcConfig,
@@ -114,54 +94,42 @@ export async function buildContextBundle(
114
94
  watch: boolean = false,
115
95
  ): Promise<void> {
116
96
  const configDir = dirname(configPath);
117
-
118
97
  const defineValues: Record<string, string> = {};
119
-
120
98
  for (const c of config.clients) {
121
99
  const normalizedC = c.toUpperCase().replace(/[^A-Z0-9]/g, "_");
122
100
  defineValues[normalizedC] = c === client ? "true" : "false";
123
101
  defineValues[`NOT_ON_${normalizedC}`] = c === client ? "false" : "true";
124
102
  defineValues[`ONLY_${normalizedC}`] = c === client ? "true" : "false";
125
103
  }
126
-
127
104
  const defineArgs = Object.entries(defineValues)
128
105
  .map(([key, value]) => `--define="${key}=${value}"`)
129
106
  .join(" ");
130
-
131
107
  const externals = getExternals(config, configDir, client);
132
108
  const externalArgs = externals.map((dep) => `--external=${dep}`).join(" ");
133
-
134
109
  const buildTarget = client === "browser" ? "browser" : "bun";
135
-
110
+ // JS output: dist/{client}/{context}/ (matches TypeScript declaration output)
136
111
  const outDirPath = join(
137
112
  configDir,
138
113
  config.outDir,
139
- context.name,
140
114
  client.toLowerCase(),
115
+ context.name,
141
116
  );
142
-
143
117
  const command = `bun build ${context.fullPath} --target=${buildTarget} --outdir=${outDirPath} ${defineArgs} ${externalArgs}${watch ? " --watch" : ""}`;
144
118
 
145
119
  return new Promise((resolve, reject) => {
146
120
  const proc = spawn(command, { shell: true, cwd: configDir });
147
-
148
121
  let output = "";
149
122
  let errorOutput = "";
150
-
151
- proc.stdout.on("data", (data) => {
123
+ proc.stdout.on("data", (data: Buffer) => {
152
124
  output += data.toString();
153
125
  });
154
-
155
- proc.stderr.on("data", (data) => {
126
+ proc.stderr.on("data", (data: Buffer) => {
156
127
  errorOutput += data.toString();
157
128
  });
158
-
159
- proc.on("close", (code) => {
160
- if (code !== 0 && !watch) {
129
+ proc.on("close", (code: number) => {
130
+ if (code !== 0 && !watch)
161
131
  reject(new Error(`Build failed: ${errorOutput || output}`));
162
- } else if (!watch) {
163
- resolve();
164
- }
132
+ else if (!watch) resolve();
165
133
  });
166
134
  });
167
135
  }
@@ -172,8 +140,8 @@ interface DeclarationResult {
172
140
  }
173
141
 
174
142
  /**
175
- * Build declarations for a context using tsgo (TypeScript Go)
176
- * Falls back to regular tsc if tsgo is not available
143
+ * Build declarations.
144
+ * Output: dist/{client}/{context}/ (matches JS output and TypeScript's natural output)
177
145
  */
178
146
  export async function buildContextDeclarations(
179
147
  configPath: string,
@@ -182,61 +150,45 @@ export async function buildContextDeclarations(
182
150
  client: string,
183
151
  ): Promise<DeclarationResult> {
184
152
  const configDir = dirname(configPath);
185
-
186
- // Generate client types
187
153
  generateClientTypes(config, configDir, client);
188
154
 
189
- const outDir = join(
190
- configDir,
191
- config.outDir,
192
- context.name,
193
- client.toLowerCase(),
194
- );
155
+ // Output to dist/{client}/ - TypeScript will add {context}/ based on source structure
156
+ const outDir = join(configDir, config.outDir, client.toLowerCase());
195
157
 
196
- // Try tsgo first, fall back to tsc
197
- const tsgoResult = await runTsgo(context.fullPath, outDir, configDir);
198
- if (tsgoResult !== null) {
199
- return tsgoResult;
200
- }
158
+ // Include arc.d.ts for global constants
159
+ const arcTypesPath = join(configDir, "arc.d.ts");
160
+ const files = existsSync(arcTypesPath)
161
+ ? [context.fullPath, arcTypesPath]
162
+ : [context.fullPath];
201
163
 
202
- // Fallback to tsc
203
- return runTsc(context.fullPath, outDir, configDir);
164
+ // Try tsgo, fallback to tsc - use configDir as rootDir
165
+ let result = await runTsgo(files, outDir, configDir);
166
+ if (result === null) {
167
+ result = await runTsc(files, outDir, configDir);
168
+ }
169
+ return result;
204
170
  }
205
171
 
206
- /**
207
- * Run tsgo CLI for declaration generation
208
- * Returns null if tsgo is not available
209
- */
210
172
  async function runTsgo(
211
- entryFile: string,
173
+ files: string[],
212
174
  outDir: string,
213
175
  cwd: string,
214
176
  ): Promise<DeclarationResult | null> {
215
- return new Promise((resolve) => {
216
- // Use npx to run tsgo from @typescript/native-preview
217
- // --ignoreConfig to skip tsconfig.json when files are specified on command line
218
- const command = `npx tsgo --declaration --emitDeclarationOnly --outDir "${outDir}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext --ignoreConfig "${entryFile}"`;
177
+ const fileArgs = files.map((f) => `"${f}"`).join(" ");
178
+ const command = `npx tsgo ${fileArgs} --declaration --emitDeclarationOnly --outDir "${outDir}" --rootDir "${cwd}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext --ignoreConfig`;
219
179
 
180
+ return new Promise((resolve) => {
220
181
  const proc = spawn(command, { shell: true, cwd });
221
-
222
182
  let output = "";
223
183
  let errorOutput = "";
224
-
225
- proc.stdout.on("data", (data) => {
184
+ proc.stdout.on("data", (data: Buffer) => {
226
185
  output += data.toString();
227
186
  });
228
-
229
- proc.stderr.on("data", (data) => {
187
+ proc.stderr.on("data", (data: Buffer) => {
230
188
  errorOutput += data.toString();
231
189
  });
232
-
233
- proc.on("error", () => {
234
- // tsgo not available
235
- resolve(null);
236
- });
237
-
238
- proc.on("close", (code) => {
239
- // Check if tsgo was not found
190
+ proc.on("error", () => resolve(null));
191
+ proc.on("close", (code: number) => {
240
192
  if (
241
193
  errorOutput.includes("not found") ||
242
194
  errorOutput.includes("ENOENT") ||
@@ -245,64 +197,40 @@ async function runTsgo(
245
197
  resolve(null);
246
198
  return;
247
199
  }
248
-
249
- const allOutput = output + errorOutput;
250
- const errors = parseTypeScriptErrors(allOutput, cwd);
251
-
252
- resolve({
253
- success: code === 0 && errors.length === 0,
254
- errors,
255
- });
200
+ const errors = parseTypeScriptErrors(output + errorOutput, cwd);
201
+ resolve({ success: code === 0 && errors.length === 0, errors });
256
202
  });
257
203
  });
258
204
  }
259
205
 
260
- /**
261
- * Run tsc CLI for declaration generation (fallback)
262
- */
263
206
  async function runTsc(
264
- entryFile: string,
207
+ files: string[],
265
208
  outDir: string,
266
209
  cwd: string,
267
210
  ): Promise<DeclarationResult> {
268
- return new Promise((resolve) => {
269
- const command = `npx tsc --declaration --emitDeclarationOnly --outDir "${outDir}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext "${entryFile}"`;
211
+ const fileArgs = files.map((f) => `"${f}"`).join(" ");
212
+ const command = `npx tsc ${fileArgs} --declaration --emitDeclarationOnly --outDir "${outDir}" --rootDir "${cwd}" --skipLibCheck --moduleResolution bundler --module esnext --target esnext`;
270
213
 
214
+ return new Promise((resolve) => {
271
215
  const proc = spawn(command, { shell: true, cwd });
272
-
273
216
  let output = "";
274
217
  let errorOutput = "";
275
-
276
- proc.stdout.on("data", (data) => {
218
+ proc.stdout.on("data", (data: Buffer) => {
277
219
  output += data.toString();
278
220
  });
279
-
280
- proc.stderr.on("data", (data) => {
221
+ proc.stderr.on("data", (data: Buffer) => {
281
222
  errorOutput += data.toString();
282
223
  });
283
-
284
- proc.on("close", (code) => {
285
- const allOutput = output + errorOutput;
286
- const errors = parseTypeScriptErrors(allOutput, cwd);
287
-
288
- resolve({
289
- success: code === 0 && errors.length === 0,
290
- errors,
291
- });
224
+ proc.on("close", (code: number) => {
225
+ const errors = parseTypeScriptErrors(output + errorOutput, cwd);
226
+ resolve({ success: code === 0 && errors.length === 0, errors });
292
227
  });
293
228
  });
294
229
  }
295
230
 
296
- /**
297
- * Parse TypeScript CLI error output into structured errors
298
- */
299
231
  function parseTypeScriptErrors(output: string, cwd: string): string[] {
300
232
  const errors: string[] = [];
301
- const lines = output.split("\n");
302
-
303
- for (const line of lines) {
304
- // Match TypeScript error format: file(line,col): error TS1234: message
305
- // or: file:line:col - error TS1234: message
233
+ for (const line of output.split("\n")) {
306
234
  const match = line.match(
307
235
  /^(.+?)(?:\((\d+),(\d+)\)|:(\d+):(\d+))\s*[-:]\s*error\s+TS\d+:\s*(.+)$/,
308
236
  );
@@ -310,33 +238,28 @@ function parseTypeScriptErrors(output: string, cwd: string): string[] {
310
238
  const file = match[1].replace(cwd + "/", "");
311
239
  const lineNum = match[2] || match[4];
312
240
  const col = match[3] || match[5];
313
- const message = match[6];
314
- errors.push(`${file}:${lineNum}:${col} - ${message}`);
241
+ errors.push(`${file}:${lineNum}:${col} - ${match[6]}`);
315
242
  } else if (line.includes("error TS")) {
316
- // Catch any other error format
317
243
  errors.push(line.trim());
318
244
  }
319
245
  }
320
-
321
246
  return errors;
322
247
  }
323
248
 
324
- // Legacy functions for backward compatibility
249
+ // Legacy
325
250
  export async function buildClient(
326
251
  configPath: string,
327
252
  config: ArcConfig,
328
253
  client: string,
329
- watch: boolean = false,
254
+ watch = false,
330
255
  ): Promise<void> {
331
256
  const configDir = dirname(configPath);
332
-
333
257
  if (config.file) {
334
258
  const context: ContextInfo = {
335
259
  name: "main",
336
260
  entryFile: config.file,
337
261
  fullPath: join(configDir, config.file),
338
262
  };
339
-
340
263
  await buildContextBundle(configPath, config, context, client, watch);
341
264
  }
342
265
  }
@@ -347,14 +270,12 @@ export async function buildDeclarations(
347
270
  client: string,
348
271
  ): Promise<void> {
349
272
  const configDir = dirname(configPath);
350
-
351
273
  if (config.file) {
352
274
  const context: ContextInfo = {
353
275
  name: "main",
354
276
  entryFile: config.file,
355
277
  fullPath: join(configDir, config.file),
356
278
  };
357
-
358
279
  await buildContextDeclarations(configPath, config, context, client);
359
280
  }
360
281
  }
@@ -113,40 +113,29 @@ export function getContextsFromConfig(
113
113
 
114
114
  /**
115
115
  * Generate base types file for TypeScript intellisense
116
- * This creates a file with all constants defined as boolean type
116
+ * This creates a .d.ts file with all constants defined as boolean type
117
117
  */
118
118
  export function generateBaseTypes(config: ArcConfig, configDir: string): void {
119
119
  const { clients } = config;
120
120
 
121
- // Create .arc directory if it doesn't exist
122
- const arcDir = join(configDir, ".arc");
123
- if (!existsSync(arcDir)) {
124
- mkdirSync(arcDir, { recursive: true });
125
- }
126
-
127
- // Generate types.ts file with boolean declarations for IDE support
128
- let typesDefs = `declare global {\n`;
121
+ // Generate ambient declarations (no export {} so they're truly global)
122
+ let typesDefs = "";
129
123
 
130
124
  clients.forEach((client) => {
131
125
  const normalizedClient = normalizeClientName(client);
132
126
 
133
- typesDefs += ` const ${normalizedClient}: boolean;\n`;
134
- typesDefs += ` const NOT_ON_${normalizedClient}: boolean;\n`;
135
- typesDefs += ` const ONLY_${normalizedClient}: boolean;\n`;
127
+ typesDefs += `declare const ${normalizedClient}: boolean;\n`;
128
+ typesDefs += `declare const NOT_ON_${normalizedClient}: boolean;\n`;
129
+ typesDefs += `declare const ONLY_${normalizedClient}: boolean;\n`;
136
130
  });
137
131
 
138
- typesDefs += `}\n\nexport {};\n`;
139
-
140
- writeFileSync(join(arcDir, "types.ts"), typesDefs);
141
-
142
- // Also generate arc.types.ts in root for backward compatibility
143
- // Only generate at root level - contexts should reference the root file
144
- writeFileSync(join(configDir, "arc.types.ts"), typesDefs);
132
+ // Write as .d.ts file in the config directory for proper TypeScript pickup
133
+ writeFileSync(join(configDir, "arc.d.ts"), typesDefs);
145
134
  }
146
135
 
147
136
  /**
148
137
  * Generate client-specific types file for a build
149
- * This creates a file with concrete true/false values based on the current client
138
+ * This creates a .d.ts file with concrete true/false values based on the current client
150
139
  */
151
140
  export function generateClientTypes(
152
141
  config: ArcConfig,
@@ -154,33 +143,31 @@ export function generateClientTypes(
154
143
  client: string,
155
144
  ): string {
156
145
  const { clients } = config;
157
- const arcDir = join(configDir, ".arc");
158
146
 
159
- // Create client-specific types directory
147
+ // Create .arc/client-types directory
148
+ const arcDir = join(configDir, ".arc");
160
149
  const clientTypesDir = join(arcDir, "client-types");
161
150
  if (!existsSync(clientTypesDir)) {
162
151
  mkdirSync(clientTypesDir, { recursive: true });
163
152
  }
164
153
 
165
- // Generate client-specific types file
166
- let typesDefs = `declare global {\n`;
154
+ // Generate client-specific ambient declarations
155
+ let typesDefs = "";
167
156
 
168
157
  clients.forEach((c) => {
169
158
  const normalizedC = normalizeClientName(c);
170
159
  const isCurrentClient = normalizeClientName(client) === normalizedC;
171
160
 
172
161
  // Set concrete values based on the current client
173
- typesDefs += ` const ${normalizedC}: ${isCurrentClient ? "true" : "false"};\n`;
174
- typesDefs += ` const NOT_ON_${normalizedC}: ${isCurrentClient ? "false" : "true"};\n`;
175
- typesDefs += ` const ONLY_${normalizedC}: ${isCurrentClient ? "true" : "false"};\n`;
162
+ typesDefs += `declare const ${normalizedC}: ${isCurrentClient ? "true" : "false"};\n`;
163
+ typesDefs += `declare const NOT_ON_${normalizedC}: ${isCurrentClient ? "false" : "true"};\n`;
164
+ typesDefs += `declare const ONLY_${normalizedC}: ${isCurrentClient ? "true" : "false"};\n`;
176
165
  });
177
166
 
178
- typesDefs += `}\n\nexport {};\n`;
179
-
180
- // Write to a client-specific file
167
+ // Write as .d.ts file
181
168
  const typesPath = join(
182
169
  clientTypesDir,
183
- `${normalizeClientName(client).toLowerCase()}.ts`,
170
+ `${normalizeClientName(client).toLowerCase()}.d.ts`,
184
171
  );
185
172
  writeFileSync(typesPath, typesDefs);
186
173