Files
windmill/backend/windmill-worker/loader.bun.windows.js
hugocasa 468aa230e5 refactor: resolve workspace imports via /f/,/u/ not $f/,$u/ aliases (#9438)
* refactor: resolve workspace imports via /f/,/u/ not $f/,$u/ aliases

Keep the CLI managed tsconfig.wmill.json / `refresh tsconfig` / Deno
import-map QoL from #9378, but re-key it on the existing /f/,/u/ workspace
paths instead of the new $f/,$u/ specifiers. Verified /f/,/u/ resolves in
tsc, Bun, Deno, the in-app ATA editor, and the worker, so the $-prefixed
alias added no value. Drop the $f/,$u/ handling from the parser, dep-map,
deno_executor, bun loaders, ATA, relative_imports and monaco paths; revert
the windmill-parser-wasm-ts bump (1.714.0 -> 1.695.0). Also fold in the
cli/package-lock.json sync for the already-committed pg-gateway dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: drop duplicate relative-path check and restore rustfmt formatting

Follow-up cleanups to the previous commit's full-file reverts, which
restored pre-#9378 state that main had since improved:

- relative_imports.ts: remove the redundant duplicate d.startsWith('/')
  (pre-#9378 had it; #9378 had repurposed that line, so main has no dup).
- windmill-parser-ts/src/lib.rs: restore the multi-line new_source_file(...)
  formatting required by backend/rustfmt.toml (the single-line revert would
  fail `cargo fmt --check`). Now differs from main only by the $f//$u/ removal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:27:24 +00:00

147 lines
5.7 KiB
JavaScript

// Injected by backend: maps normalized paths to temp storage hashes (or null)
const TEMP_SCRIPT_REFS = TEMP_SCRIPT_REFS_PLACEHOLDER;
// Windows-specific bun loader that uses a virtual "windmill-url" namespace instead
// of writing .url files to disk. This avoids Windows path issues (backslashes in
// resolve(), 8.3 short filenames, drive letter prefixes). The virtual namespace
// approach is likely better on all fronts but we keep the original .url-file loader
// on Linux to avoid breaking back-compat.
const p = {
name: "windmill-relative-resolver",
async setup(build) {
const { readFileSync } = await import("fs");
const { resolve } = await import("node:path");
const base_internal_url = "BASE_INTERNAL_URL".replace(
"localhost",
"127.0.0.1"
);
const w_id = "W_ID";
const current_path = "CURRENT_PATH";
const token = "TOKEN";
const cdir = resolve("./");
const cdirNoPrivate = cdir.replace(/^\/private/, ""); // for macos
// Normalize path to forward slashes to match Bun's resolver output on Windows
const cdirFwd = cdir.replace(/\\/g, "/");
const cdirPosix = cdirFwd.replace(/^[a-zA-Z]:/, "");
const filterResolve = new RegExp(
`^(?!\\.\/main\\.ts)(?!\\.\/_wm_)(?!${cdirFwd}\/main\\.ts)(?!${cdirFwd}\/_wm_)(?!${cdirPosix}\/main\\.ts)(?!${cdirPosix}\/_wm_)(?!(?:/private)?${cdirNoPrivate}\/wrapper\\.mjs).*\\.ts$`
);
let cdirNodeModules = `${cdirFwd}/node_modules/`;
const filterLoad = new RegExp(`^${cdir}\/main\\.ts$`);
const transpiler = new Bun.Transpiler({
loader: "ts",
});
function replaceRelativeImports(code) {
const imports = transpiler.scanImports(code);
for (const imp of imports) {
if (imp.kind == "import-statement") {
if (
(imp.path.startsWith(".") ||
imp.path.startsWith("/u/") ||
imp.path.startsWith("/f/")) &&
!imp.path.endsWith(".ts")
) {
code = code.replaceAll(imp.path, imp.path + ".ts");
}
}
}
return {
contents: code,
};
}
function normalizePath(rawPath) {
return rawPath.split("/").reduce((acc, seg) => {
if (seg === "..") acc.pop();
else if (seg !== "." && seg !== "") acc.push(seg);
return acc;
}, []).join("/");
}
// Resolve a windmill script import path relative to an importer path.
// Bun on Windows may prefix args with "windmill-url:" or strip leading "/".
function resolveWindmillImport(importerPath, importPath) {
const path = importPath.replace(/^windmill-url:/, "").replace(/^\//, "");
const isAbsolute = path.startsWith("f/") || path.startsWith("u/");
const endExt = path.endsWith(".ts") ? "" : ".ts";
const rawScriptPath = isAbsolute
? `${path}${endExt}`
: `${importerPath}/../${path}${endExt}`;
const normalized = normalizePath(rawScriptPath);
// Look up temp script hash (keys are extensionless paths)
const lookupPath = normalized.replace(/\.ts$/, "");
const hash = TEMP_SCRIPT_REFS?.[lookupPath];
// Encode hash in the path so onLoad can extract it and append to fetch URL
const resolvedPath = hash ? `${normalized}?temp_script_hash=${hash}` : normalized;
return { path: resolvedPath, namespace: "windmill-url" };
}
build.onLoad({ filter: filterLoad }, async (args) => {
const code = readFileSync(args.path, "utf8");
return replaceRelativeImports(code);
});
// Load windmill scripts by fetching from the API
build.onLoad({ filter: /.*/, namespace: "windmill-url" }, async (args) => {
// Extract temp_script_hash if embedded in the path by resolveWindmillImport
const [scriptPath, queryString] = args.path.replace(/^windmill-url:/, "").split("?");
const hashParam = queryString?.startsWith("temp_script_hash=")
? queryString.replace("temp_script_hash=", "")
: undefined;
const url = `${base_internal_url}/api/w/${w_id}/scripts/RAW_GET_ENDPOINT/p/${scriptPath}`
+ (hashParam ? `?temp_script_hash=${hashParam}` : "");
const req = await fetch(url, {
method: "GET",
headers: {
Authorization: "Bearer " + token,
},
});
if (!req.ok) {
throw new Error(
`Failed to find relative import at ${url} (status ${req.status})`
);
}
const contents = await req.text();
return {
contents: replaceRelativeImports(contents).contents,
loader: "tsx",
};
});
// Resolve windmill script imports from the file namespace (e.g. from main.ts)
build.onResolve({ filter: filterResolve }, (args) => {
const importerFwd = args.importer?.replace(/\\/g, "/") ?? "";
if (importerFwd.startsWith(cdirNodeModules)) {
return undefined;
}
// Check if the import resolves to a local module file (written by write_module_files)
if (args.path.startsWith(".")) {
const cwdPath = resolve(cdir, args.path);
try {
readFileSync(cwdPath);
return { path: cwdPath };
} catch {}
}
const isMainTs =
args.importer == "./main.ts" || importerFwd.endsWith("/main.ts");
const file_path = isMainTs
? current_path
: importerFwd.replace(cdirFwd + "/", "");
return resolveWindmillImport(file_path, args.path);
});
// Resolve nested imports from within windmill-url modules
build.onResolve({ filter: /\.ts$/, namespace: "windmill-url" }, (args) => {
// Strip any query string from the importer path before resolving
const importer = args.importer.replace(/^windmill-url:/, "").split("?")[0];
return resolveWindmillImport(importer, args.path);
});
},
};