vize 0.90.0 → 0.91.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/src/config.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as fs from "node:fs";
2
- import * as os from "node:os";
3
2
  import * as path from "node:path";
4
3
  import { execFileSync } from "node:child_process";
5
4
  import { fileURLToPath, pathToFileURL } from "node:url";
6
5
  import { transform } from "oxc-transform";
7
6
  import type {
7
+ LanguageServerConfig,
8
8
  VizeConfig,
9
9
  LoadConfigOptions,
10
10
  UserConfigExport,
@@ -14,10 +14,10 @@ import type {
14
14
  } from "./types/index.js";
15
15
 
16
16
  export const CONFIG_FILE_NAMES = [
17
+ "vize.config.pkl",
17
18
  "vize.config.ts",
18
19
  "vize.config.js",
19
20
  "vize.config.mjs",
20
- "vize.config.pkl",
21
21
  "vize.config.json",
22
22
  ] as const;
23
23
 
@@ -36,6 +36,10 @@ export const VIZE_CONFIG_JSON_SCHEMA_PATH = path.join(
36
36
 
37
37
  export const VIZE_CONFIG_PKL_SCHEMA_PATH = path.join(PACKAGE_ROOT, "pkl", "vize.pkl");
38
38
 
39
+ type CompatVizeConfig = VizeConfig & {
40
+ lsp?: LanguageServerConfig;
41
+ };
42
+
39
43
  /**
40
44
  * Define a Vize configuration with type checking.
41
45
  * Accepts a plain object or a function that receives ConfigEnv.
@@ -66,38 +70,37 @@ export async function loadConfig(
66
70
  }
67
71
 
68
72
  if (mode === "auto") {
69
- const configPath = findConfigFileAuto(root);
70
- if (!configPath) {
71
- return null;
72
- }
73
- return loadConfigFile(configPath, env);
73
+ return loadConfigFromDirAuto(root, env);
74
74
  }
75
75
 
76
- const configPath = findConfigFileInDir(root);
77
- if (!configPath) {
78
- return null;
79
- }
80
-
81
- return loadConfigFile(configPath, env);
76
+ return loadConfigFromDir(root, env);
82
77
  }
83
78
 
84
- function findConfigFileInDir(dir: string): string | null {
79
+ async function loadConfigFromDir(dir: string, env?: ConfigEnv): Promise<VizeConfig | null> {
85
80
  for (const name of CONFIG_FILE_NAMES) {
86
81
  const filePath = path.join(dir, name);
87
- if (fs.existsSync(filePath)) {
88
- return filePath;
82
+ if (!fs.existsSync(filePath)) {
83
+ continue;
84
+ }
85
+
86
+ const config = await loadConfigFile(filePath, env);
87
+ if (config !== null) {
88
+ return config;
89
89
  }
90
90
  }
91
91
  return null;
92
92
  }
93
93
 
94
- function findConfigFileAuto(startDir: string): string | null {
94
+ async function loadConfigFromDirAuto(
95
+ startDir: string,
96
+ env?: ConfigEnv,
97
+ ): Promise<VizeConfig | null> {
95
98
  let currentDir = path.resolve(startDir);
96
99
 
97
100
  while (true) {
98
- const configPath = findConfigFileInDir(currentDir);
99
- if (configPath) {
100
- return configPath;
101
+ const config = await loadConfigFromDir(currentDir, env);
102
+ if (config !== null) {
103
+ return config;
101
104
  }
102
105
 
103
106
  const parentDir = path.dirname(currentDir);
@@ -110,26 +113,81 @@ function findConfigFileAuto(startDir: string): string | null {
110
113
  }
111
114
 
112
115
  async function loadConfigFile(filePath: string, env?: ConfigEnv): Promise<VizeConfig | null> {
113
- if (!fs.existsSync(filePath)) {
116
+ const absolutePath = path.resolve(filePath);
117
+ if (!fs.existsSync(absolutePath)) {
114
118
  return null;
115
119
  }
116
120
 
117
- const ext = path.extname(filePath);
121
+ const ext = path.extname(absolutePath);
118
122
 
119
- if (ext === ".json") {
120
- const content = fs.readFileSync(filePath, "utf-8");
121
- return parseJsonConfig(content, filePath);
123
+ if (ext === ".pkl") {
124
+ return loadPklConfig(absolutePath);
122
125
  }
123
126
 
124
- if (ext === ".pkl") {
125
- return loadPklConfig(filePath);
127
+ if (ext === ".json") {
128
+ const content = fs.readFileSync(absolutePath, "utf-8");
129
+ return parseJsonConfig(content, absolutePath);
126
130
  }
127
131
 
128
132
  if (ext === ".ts") {
129
- return loadTypeScriptConfig(filePath, env);
133
+ return loadTypeScriptConfig(absolutePath, env);
134
+ }
135
+
136
+ return loadESMConfig(absolutePath, env);
137
+ }
138
+
139
+ function findPklBinary(): string | null {
140
+ try {
141
+ const pklPkgPath = import.meta.resolve?.("@pkl-community/pkl");
142
+ if (pklPkgPath) {
143
+ const pklLibDir = path.dirname(fileURLToPath(pklPkgPath));
144
+ const pklPackageDir = path.dirname(pklLibDir);
145
+ const candidates = [
146
+ path.join(pklLibDir, "main.js"),
147
+ path.join(pklPackageDir, "pkl"),
148
+ path.join(pklPackageDir, "pkl.exe"),
149
+ ];
150
+
151
+ for (const candidate of candidates) {
152
+ if (fs.existsSync(candidate)) {
153
+ return candidate;
154
+ }
155
+ }
156
+ }
157
+ } catch {
158
+ // Fall back to PATH below.
159
+ }
160
+
161
+ try {
162
+ execFileSync("pkl", ["--version"], { stdio: "ignore" });
163
+ return "pkl";
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
168
+
169
+ function loadPklConfig(filePath: string): VizeConfig | null {
170
+ const pklBin = findPklBinary();
171
+ if (!pklBin) {
172
+ console.warn(
173
+ "[vize] pkl CLI not found. Install @pkl-community/pkl or add pkl to PATH. " +
174
+ "Falling back to the next config format.",
175
+ );
176
+ return null;
130
177
  }
131
178
 
132
- return loadESMConfig(filePath, env);
179
+ try {
180
+ const output = execFileSync(pklBin, ["eval", "-f", "json", filePath], {
181
+ cwd: path.dirname(filePath),
182
+ encoding: "utf-8",
183
+ stdio: ["ignore", "pipe", "pipe"],
184
+ timeout: 30_000,
185
+ });
186
+ return parseJsonConfig(output, filePath);
187
+ } catch (error) {
188
+ console.warn(`[vize] Failed to evaluate ${filePath}: ${getErrorMessage(error)}`);
189
+ return null;
190
+ }
133
191
  }
134
192
 
135
193
  async function resolveConfigExport(
@@ -137,10 +195,10 @@ async function resolveConfigExport(
137
195
  env?: ConfigEnv,
138
196
  ): Promise<VizeConfig> {
139
197
  if (typeof exported === "function") {
140
- return exported(env ?? DEFAULT_CONFIG_ENV);
198
+ return normalizeLoadedConfig(await exported(env ?? DEFAULT_CONFIG_ENV));
141
199
  }
142
200
 
143
- return exported;
201
+ return normalizeLoadedConfig(exported);
144
202
  }
145
203
 
146
204
  async function loadTypeScriptConfig(filePath: string, env?: ConfigEnv): Promise<VizeConfig> {
@@ -151,8 +209,10 @@ async function loadTypeScriptConfig(filePath: string, env?: ConfigEnv): Promise<
151
209
  },
152
210
  });
153
211
 
154
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "vize-config-"));
155
- const tempFile = path.join(tempDir, "config.mjs");
212
+ const tempFile = path.join(
213
+ path.dirname(filePath),
214
+ `.vize-config-${process.pid}-${Date.now()}.mjs`,
215
+ );
156
216
  fs.writeFileSync(tempFile, result.code, { flag: "wx", mode: 0o600 });
157
217
 
158
218
  try {
@@ -160,7 +220,7 @@ async function loadTypeScriptConfig(filePath: string, env?: ConfigEnv): Promise<
160
220
  const exported: UserConfigExport = module.default || module;
161
221
  return resolveConfigExport(exported, env);
162
222
  } finally {
163
- fs.rmSync(tempDir, { recursive: true, force: true });
223
+ fs.rmSync(tempFile, { force: true });
164
224
  }
165
225
  }
166
226
 
@@ -170,21 +230,6 @@ async function loadESMConfig(filePath: string, env?: ConfigEnv): Promise<VizeCon
170
230
  return resolveConfigExport(exported, env);
171
231
  }
172
232
 
173
- function loadPklConfig(filePath: string): VizeConfig {
174
- try {
175
- const output = execFileSync("pkl", ["eval", "--format", "json", filePath], {
176
- cwd: path.dirname(filePath),
177
- encoding: "utf-8",
178
- stdio: ["ignore", "pipe", "pipe"],
179
- });
180
- return parseJsonConfig(output, filePath);
181
- } catch (error) {
182
- throw new Error(
183
- `Failed to evaluate PKL config at ${filePath}. Make sure the 'pkl' CLI is installed and on PATH. ${getErrorMessage(error)}`,
184
- );
185
- }
186
- }
187
-
188
233
  async function importFresh(filePath: string): Promise<Record<string, unknown>> {
189
234
  const fileUrl = pathToFileURL(filePath);
190
235
  fileUrl.searchParams.set("t", String(fs.statSync(filePath).mtimeMs));
@@ -201,7 +246,7 @@ function parseJsonConfig(content: string, filePath: string): VizeConfig {
201
246
 
202
247
  function normalizeLoadedConfig(config: unknown): VizeConfig {
203
248
  const normalized = stripNullish(config);
204
- return (normalized ?? {}) as VizeConfig;
249
+ return normalizeConfigAliases((normalized ?? {}) as CompatVizeConfig);
205
250
  }
206
251
 
207
252
  function stripNullish(value: unknown): unknown {
@@ -241,8 +286,16 @@ function getErrorMessage(error: unknown): string {
241
286
  export function normalizeGlobalTypes(
242
287
  config: GlobalTypesConfig,
243
288
  ): Record<string, GlobalTypeDeclaration> {
289
+ const resolvedConfig =
290
+ "types" in config &&
291
+ typeof config.types === "object" &&
292
+ config.types !== null &&
293
+ !Array.isArray(config.types)
294
+ ? config.types
295
+ : config;
296
+
244
297
  const result: Record<string, GlobalTypeDeclaration> = {};
245
- for (const [key, value] of Object.entries(config)) {
298
+ for (const [key, value] of Object.entries(resolvedConfig)) {
246
299
  if (typeof value === "string") {
247
300
  result[key] = { type: value };
248
301
  } else {
@@ -251,3 +304,19 @@ export function normalizeGlobalTypes(
251
304
  }
252
305
  return result;
253
306
  }
307
+
308
+ function normalizeConfigAliases(config: CompatVizeConfig): VizeConfig {
309
+ if (config.lsp === undefined) {
310
+ return config;
311
+ }
312
+
313
+ const { lsp, ...rest } = config;
314
+ if (config.languageServer !== undefined) {
315
+ return rest;
316
+ }
317
+
318
+ return {
319
+ ...rest,
320
+ languageServer: lsp,
321
+ };
322
+ }
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export type {
14
14
  LinterConfig,
15
15
  TypeCheckerConfig,
16
16
  FormatterConfig,
17
+ LanguageServerConfig,
17
18
  LspConfig,
18
19
  MuseaConfig,
19
20
  MuseaVrtConfig,