mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 08:01:26 +00:00
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>
This commit is contained in:
@@ -197,10 +197,8 @@ pub fn parse_relative_imports(code: &str, path: &str) -> anyhow::Result<Vec<Stri
|
||||
// Remove .ts extension if present
|
||||
let imp = imp.strip_suffix(".ts").unwrap_or(&imp);
|
||||
|
||||
if imp.starts_with("/") || imp.starts_with("$f/") || imp.starts_with("$u/") {
|
||||
// Absolute path (e.g., /f/folder/script) or its local-friendly alias
|
||||
// (e.g., $f/folder/script) - strip the leading `/` or `$` to get the
|
||||
// workspace-rooted path.
|
||||
if imp.starts_with("/") {
|
||||
// Absolute path (e.g., /f/folder/script) - remove leading slash
|
||||
imp[1..].to_string()
|
||||
} else {
|
||||
// Relative path (e.g., ./script or ../folder/script)
|
||||
@@ -215,14 +213,9 @@ pub fn parse_relative_imports(code: &str, path: &str) -> anyhow::Result<Vec<Stri
|
||||
Ok(resolved)
|
||||
}
|
||||
|
||||
/// Check if an import path is a relative import (starts with `./`, `../`, `/`, or the
|
||||
/// local-friendly workspace aliases `$f/`/`$u/`)
|
||||
/// Check if an import path is a relative import (starts with `./`, `../`, or `/`)
|
||||
fn is_relative_import(import_path: &str) -> bool {
|
||||
import_path.starts_with("./")
|
||||
|| import_path.starts_with("../")
|
||||
|| import_path.starts_with("/")
|
||||
|| import_path.starts_with("$f/")
|
||||
|| import_path.starts_with("$u/")
|
||||
import_path.starts_with("./") || import_path.starts_with("../") || import_path.starts_with("/")
|
||||
}
|
||||
|
||||
/// Normalize a path by resolving `.` and `..` components
|
||||
|
||||
@@ -891,40 +891,18 @@ mod tests {
|
||||
assert_eq!(result, vec!["f/shared/utils"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relative_imports_dollar_alias() {
|
||||
// `$f/`/`$u/` are local-friendly aliases for the absolute workspace paths
|
||||
// `/f/`/`/u/` and must resolve to the same workspace-rooted path.
|
||||
let code = r#"
|
||||
import { shared } from "$f/shared/utils.ts";
|
||||
import { mine } from "$u/me/lib";
|
||||
export async function main() { return shared() + mine(); }
|
||||
"#;
|
||||
let result = parse_relative_imports(code, "f/folder/script").unwrap();
|
||||
assert_eq!(result, vec!["f/shared/utils", "u/me/lib"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relative_imports_mixed() {
|
||||
let code = r#"
|
||||
import { helper } from "./helper";
|
||||
import { utils } from "../utils";
|
||||
import { shared } from "/f/shared/lib";
|
||||
import { aliased } from "$f/shared/aliased.ts";
|
||||
import lodash from "lodash";
|
||||
export async function main() { return helper() + utils() + shared() + aliased(); }
|
||||
export async function main() { return helper() + utils() + shared(); }
|
||||
"#;
|
||||
let result = parse_relative_imports(code, "f/folder/script").unwrap();
|
||||
// Should only include relative imports, not external packages like lodash
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![
|
||||
"f/folder/helper",
|
||||
"f/shared/aliased",
|
||||
"f/shared/lib",
|
||||
"f/utils"
|
||||
]
|
||||
);
|
||||
assert_eq!(result, vec!["f/folder/helper", "f/shared/lib", "f/utils"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -4763,28 +4763,6 @@ export async function main() {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_bun"))]
|
||||
async fn test_relative_imports_bun_dollar_alias(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// `$f/` is a local-friendly alias for the absolute workspace path `/f/` and must
|
||||
// resolve identically on the worker. Mix it with relative imports to confirm both
|
||||
// paths coexist.
|
||||
let content = r#"
|
||||
import { main as test1 } from "$f/system/same_folder_script.ts";
|
||||
import { main as test2 } from "./same_folder_script.ts";
|
||||
import { main as test3 } from "$f/system_relative/different_folder_script.ts";
|
||||
import { main as test4 } from "../system_relative/different_folder_script.ts";
|
||||
|
||||
export async function main() {
|
||||
return [test1(), test2(), test3(), test4()];
|
||||
}
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Bun).await?;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Bun).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "relative_bun"))]
|
||||
async fn test_nested_imports_bun(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
@@ -4821,27 +4799,6 @@ export async function main() {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_deno"))]
|
||||
async fn test_relative_imports_deno_dollar_alias(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// `$f/` is a local-friendly alias for the absolute workspace path `/f/` and must
|
||||
// resolve identically via the Deno import map. Mix it with relative imports.
|
||||
let content = r#"
|
||||
import { main as test1 } from "$f/system/same_folder_script.ts";
|
||||
import { main as test2 } from "./same_folder_script.ts";
|
||||
import { main as test3 } from "$f/system_relative/different_folder_script.ts";
|
||||
import { main as test4 } from "../system_relative/different_folder_script.ts";
|
||||
|
||||
export async function main() {
|
||||
return [test1(), test2(), test3(), test4()];
|
||||
}
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
run_deployed_relative_imports(&db, content.clone(), ScriptLang::Deno).await?;
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Deno).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "relative_deno"))]
|
||||
async fn test_nested_imports_deno(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let content = r#"
|
||||
|
||||
@@ -50,11 +50,6 @@ fn parse_ts_relative_imports(
|
||||
let import = import.trim_end_matches(".ts");
|
||||
if import.starts_with("/") {
|
||||
relative_imports.push(import.trim_start_matches("/").to_string());
|
||||
} else if import.starts_with("$f/") || import.starts_with("$u/") {
|
||||
// `$f/...`/`$u/...` are local-friendly aliases for the absolute workspace
|
||||
// paths `/f/...`/`/u/...`. Stripping the leading `$` yields the same
|
||||
// workspace-rooted path the rest of the dependency machinery expects.
|
||||
relative_imports.push(import.trim_start_matches("$").to_string());
|
||||
} else if import.starts_with(".") {
|
||||
let normalized = try_normalize(std::path::Path::new(&format!(
|
||||
"{}/../{}",
|
||||
|
||||
@@ -915,7 +915,7 @@ pub async fn run_deployed_relative_imports(
|
||||
.unwrap();
|
||||
|
||||
// Regression guard for the Deno lock-gen import map (generate_deno_lock):
|
||||
// it must resolve the `$f/`/`$u/` aliases, otherwise `deno cache --lock`
|
||||
// it must resolve workspace `/f/`/`/u/` imports, otherwise `deno cache --lock`
|
||||
// fails with "not a dependency and not in import map". We match that
|
||||
// specific failure rather than asserting lock_error_logs is empty —
|
||||
// the field also captures benign, non-fatal lock-job output (e.g. Bun's
|
||||
@@ -930,7 +930,7 @@ pub async fn run_deployed_relative_imports(
|
||||
if let Some(err) = &lock_error {
|
||||
assert!(
|
||||
!err.contains("not in import map"),
|
||||
"lock generation failed to resolve a workspace import (likely $f//$u/): {err}"
|
||||
"lock generation failed to resolve a workspace import: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,7 @@ const p = {
|
||||
if (
|
||||
(imp.path.startsWith(".") ||
|
||||
imp.path.startsWith("/u/") ||
|
||||
imp.path.startsWith("/f/") ||
|
||||
imp.path.startsWith("$u/") ||
|
||||
imp.path.startsWith("$f/")) &&
|
||||
imp.path.startsWith("/f/")) &&
|
||||
!imp.path.endsWith(".ts")
|
||||
) {
|
||||
code = code.replaceAll(imp.path, imp.path + ".ts");
|
||||
@@ -99,29 +97,21 @@ const p = {
|
||||
? current_path
|
||||
: args.importer.replace(cdir + "/", "");
|
||||
|
||||
// `$f/...`/`$u/...` are local-friendly aliases for the absolute workspace
|
||||
// paths `/f/...`/`/u/...`; rewrite to the `/`-prefixed form so the absolute
|
||||
// resolution branch below handles them identically.
|
||||
const importPath =
|
||||
args.path.startsWith("$f/") || args.path.startsWith("$u/")
|
||||
? "/" + args.path.slice(1)
|
||||
: args.path;
|
||||
|
||||
const isRelative = !importPath.startsWith("/");
|
||||
const endExt = importPath.endsWith(".ts") ? "" : ".ts";
|
||||
const pathNoExt = importPath.replace(/\.ts$/, "");
|
||||
const isRelative = !args.path.startsWith("/");
|
||||
const endExt = args.path.endsWith(".ts") ? "" : ".ts";
|
||||
const pathNoExt = args.path.replace(/\.ts$/, "");
|
||||
|
||||
// Lookup temp script hash
|
||||
const normalized = (isRelative ? join(dirname(file_path), pathNoExt) : pathNoExt.slice(1)).replace(/\\/g, "/");
|
||||
const hash = TEMP_SCRIPT_REFS?.[normalized];
|
||||
|
||||
const url = (isRelative
|
||||
? `${base_internal_url}/api/w/${w_id}/scripts/raw_unpinned/p/${file_path}/../${importPath}${endExt}`
|
||||
: `${base_internal_url}/api/w/${w_id}/scripts/raw_unpinned/p/${importPath}${endExt}`
|
||||
? `${base_internal_url}/api/w/${w_id}/scripts/raw_unpinned/p/${file_path}/../${args.path}${endExt}`
|
||||
: `${base_internal_url}/api/w/${w_id}/scripts/raw_unpinned/p/${args.path}${endExt}`
|
||||
) + (hash ? `?temp_script_hash=${hash}` : "");
|
||||
const file = isRelative
|
||||
? resolve("./" + file_path + "/../" + importPath + ".url")
|
||||
: resolve("./" + importPath + ".url");
|
||||
? resolve("./" + file_path + "/../" + args.path + ".url")
|
||||
: resolve("./" + args.path + ".url");
|
||||
mkdirSync(dirname(file), { recursive: true });
|
||||
writeFileSync(file, url);
|
||||
return {
|
||||
|
||||
@@ -44,9 +44,7 @@ const p = {
|
||||
if (
|
||||
(imp.path.startsWith(".") ||
|
||||
imp.path.startsWith("/u/") ||
|
||||
imp.path.startsWith("/f/") ||
|
||||
imp.path.startsWith("$u/") ||
|
||||
imp.path.startsWith("$f/")) &&
|
||||
imp.path.startsWith("/f/")) &&
|
||||
!imp.path.endsWith(".ts")
|
||||
) {
|
||||
code = code.replaceAll(imp.path, imp.path + ".ts");
|
||||
@@ -69,13 +67,7 @@ const p = {
|
||||
// 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) {
|
||||
// Strip the "windmill-url:" namespace prefix, an optional leading "/"
|
||||
// (absolute `/f/`,`/u/`), and the local-friendly "$" alias prefix
|
||||
// (`$f/`,`$u/`) so absolute workspace imports normalize to `f/`,`u/`.
|
||||
const path = importPath
|
||||
.replace(/^windmill-url:/, "")
|
||||
.replace(/^\//, "")
|
||||
.replace(/^\$(?=[fu]\/)/, "");
|
||||
const path = importPath.replace(/^windmill-url:/, "").replace(/^\//, "");
|
||||
const isAbsolute = path.startsWith("f/") || path.startsWith("u/");
|
||||
const endExt = path.endsWith(".ts") ? "" : ".ts";
|
||||
const rawScriptPath = isAbsolute
|
||||
|
||||
@@ -170,15 +170,10 @@ pub async fn generate_deno_lock(
|
||||
let _ = write_file(job_dir, "main.ts", code)?;
|
||||
|
||||
let import_map_path = format!("{job_dir}/import_map.json");
|
||||
// `$f/`/`$u/` are local-friendly aliases for the absolute workspace paths `/f/`,`/u/`.
|
||||
// The runtime import map (`build_import_map`) maps them too; without these keys here,
|
||||
// `deno cache --lock` can't resolve `$f/...` imports and lock generation fails.
|
||||
let import_map = format!(
|
||||
r#"{{
|
||||
"imports": {{
|
||||
"/": "{base_internal_url}/api/scripts_u/empty_ts/",
|
||||
"$f/": "{base_internal_url}/api/scripts_u/empty_ts/f/",
|
||||
"$u/": "{base_internal_url}/api/scripts_u/empty_ts/u/"
|
||||
"/": "{base_internal_url}/api/scripts_u/empty_ts/"
|
||||
}}
|
||||
}}"#,
|
||||
);
|
||||
@@ -586,8 +581,6 @@ pub(crate) async fn build_import_map(
|
||||
"{base_internal_url}/api/w/{w_id}/scripts/raw/p/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"{base_internal_url}": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"$f/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/f/",
|
||||
"$u/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/u/",
|
||||
"./wrapper.ts": "./wrapper.ts",
|
||||
"./main.ts": "./main.ts"{relative_mounts}
|
||||
{extra_import_map}
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@
|
||||
"windmill-parser-wasm-regex": "1.692.0",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.714.0",
|
||||
"windmill-parser-wasm-ts": "1.695.0",
|
||||
"windmill-parser-wasm-yaml": "1.593.0",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"ws": "8.18.0",
|
||||
@@ -308,7 +308,7 @@
|
||||
|
||||
"windmill-parser-wasm-rust": ["windmill-parser-wasm-rust@1.647.1", "", {}, "sha512-9yGLYZX2Hn9TdTqGY/5Fp50ftzgUsrfBkSK9vJkKJd5Amyg+yXLBGzd8pz6Org+4uxMenz/16wpsgijvo6uhhQ=="],
|
||||
|
||||
"windmill-parser-wasm-ts": ["windmill-parser-wasm-ts@1.714.0", "", {}, "sha512-IvkR+tMupzIviST6IahLyNt+feK1A6ZBFUGHWl0NwJQUT0FHdjSk1BmSULSVdwSXTvvp+xcd2WrkB9pBEM5Giw=="],
|
||||
"windmill-parser-wasm-ts": ["windmill-parser-wasm-ts@1.695.0", "", {}, "sha512-9EFxeRZWmfb7EyhSlcG7dzTTKETPRYAvpRlxxLkhhtI5I219wFgI7kwrMpz4stXHJj/aqBknVv66NQHRstSJmw=="],
|
||||
|
||||
"windmill-parser-wasm-yaml": ["windmill-parser-wasm-yaml@1.593.0", "", {}, "sha512-Gyx4aR2jsJYuDrD3mCNTmz7LWOQQXPw5yKNCC1xRgUOPfjsD/tINAFfsBLwVOSmlQQcFZO+wHm4KtDtXOcnGVw=="],
|
||||
|
||||
|
||||
Generated
+11
-4
@@ -17,6 +17,7 @@
|
||||
"jszip": "3.8.0",
|
||||
"minimatch": "^10.0.0",
|
||||
"open": "^10.0.0",
|
||||
"pg-gateway": "0.3.0-beta.4",
|
||||
"svelte": "^5.45.2",
|
||||
"tar-stream": "^3.1.7",
|
||||
"windmill-parser-wasm-csharp": "1.510.1",
|
||||
@@ -29,7 +30,7 @@
|
||||
"windmill-parser-wasm-regex": "1.692.0",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.714.0",
|
||||
"windmill-parser-wasm-ts": "1.695.0",
|
||||
"windmill-parser-wasm-yaml": "1.593.0",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"ws": "8.18.0",
|
||||
@@ -1236,6 +1237,12 @@
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/pg-gateway": {
|
||||
"version": "0.3.0-beta.4",
|
||||
"resolved": "https://registry.npmjs.org/pg-gateway/-/pg-gateway-0.3.0-beta.4.tgz",
|
||||
"integrity": "sha512-CTjsM7Z+0Nx2/dyZ6r8zRsc3f9FScoD5UAOlfUx1Fdv/JOIWvRbF7gou6l6vP+uypXQVoYPgw8xZDXgMGvBa4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
@@ -1453,9 +1460,9 @@
|
||||
"integrity": "sha512-9yGLYZX2Hn9TdTqGY/5Fp50ftzgUsrfBkSK9vJkKJd5Amyg+yXLBGzd8pz6Org+4uxMenz/16wpsgijvo6uhhQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-ts": {
|
||||
"version": "1.714.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.714.0.tgz",
|
||||
"integrity": "sha512-IvkR+tMupzIviST6IahLyNt+feK1A6ZBFUGHWl0NwJQUT0FHdjSk1BmSULSVdwSXTvvp+xcd2WrkB9pBEM5Giw=="
|
||||
"version": "1.695.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.695.0.tgz",
|
||||
"integrity": "sha512-9EFxeRZWmfb7EyhSlcG7dzTTKETPRYAvpRlxxLkhhtI5I219wFgI7kwrMpz4stXHJj/aqBknVv66NQHRstSJmw=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-yaml": {
|
||||
"version": "1.593.0",
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@
|
||||
"windmill-parser-wasm-regex": "1.692.0",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.714.0",
|
||||
"windmill-parser-wasm-ts": "1.695.0",
|
||||
"windmill-parser-wasm-yaml": "1.593.0",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"ws": "8.18.0",
|
||||
|
||||
@@ -11,16 +11,13 @@ import * as log from "../../core/log.ts";
|
||||
import { readConfigFile } from "../../core/conf.ts";
|
||||
|
||||
/**
|
||||
* Local-friendly aliases for the absolute workspace import paths `/f/...` and
|
||||
* `/u/...`. Unlike the `/`-prefixed form (which every local tool treats as a
|
||||
* filesystem-root path), `$f/`/`$u/` are bare specifiers that can be remapped to
|
||||
* the on-disk `f/`/`u/` folders via tsconfig `paths` and Deno import maps, so the
|
||||
* same import resolves both on the Windmill worker and in a local editor.
|
||||
* The on-disk folders (`f/`, `u/`) that the absolute workspace import paths
|
||||
* `/f/...` and `/u/...` map to. tsconfig `paths` and Deno import maps remap the
|
||||
* `/f/`,`/u/` prefixes to these local folders, so the same workspace import
|
||||
* resolves both on the Windmill worker and in a local editor (tsc/Bun/Deno all
|
||||
* honor the `/`-prefixed key).
|
||||
*/
|
||||
const WORKSPACE_IMPORT_ALIASES: Record<string, string> = {
|
||||
$f: "f",
|
||||
$u: "u",
|
||||
};
|
||||
const WORKSPACE_IMPORT_DIRS = ["f", "u"];
|
||||
|
||||
// wmill-managed files holding the recommended config. They are always
|
||||
// (re)written so we can ship updated recommendations over time; users keep
|
||||
@@ -51,25 +48,25 @@ function buildManagedTsconfig(): {
|
||||
compilerOptions: Record<string, unknown>;
|
||||
include: string[];
|
||||
} {
|
||||
// Map "$f/*" -> ["./f/*"], "$u/*" -> ["./u/*"] so the editor resolves
|
||||
// Map "/f/*" -> ["./f/*"], "/u/*" -> ["./u/*"] so the editor resolves
|
||||
// workspace imports against the local script folders.
|
||||
//
|
||||
// Known limitation: this resolves imports written with a plain `.ts` extension
|
||||
// (the canonical form). Scripts stored with a flavor-specific extension —
|
||||
// `.bun.ts`/`.deno.ts`/`.fetch.ts` for languages other than the project default
|
||||
// (see filePathExtensionFromContentType) — won't resolve via these aliases in a
|
||||
// (see filePathExtensionFromContentType) — won't resolve via these `paths` in a
|
||||
// local editor. The worker (extension-agnostic API) and the in-app editor (ATA
|
||||
// normalizes to `.ts`) handle those fine; only local tsc / VS Code is affected.
|
||||
const paths: Record<string, string[]> = {};
|
||||
for (const [alias, dir] of Object.entries(WORKSPACE_IMPORT_ALIASES)) {
|
||||
paths[`${alias}/*`] = [`./${dir}/*`];
|
||||
for (const dir of WORKSPACE_IMPORT_DIRS) {
|
||||
paths[`/${dir}/*`] = [`./${dir}/*`];
|
||||
}
|
||||
return {
|
||||
compilerOptions: {
|
||||
target: "ESNext",
|
||||
module: "ESNext",
|
||||
moduleResolution: "bundler",
|
||||
// Workspace imports carry an explicit `.ts` extension (e.g. "$f/foo/bar.ts");
|
||||
// Workspace imports carry an explicit `.ts` extension (e.g. "/f/foo/bar.ts");
|
||||
// allow it so the editor doesn't flag every cross-script import.
|
||||
allowImportingTsExtensions: true,
|
||||
noEmit: true,
|
||||
@@ -147,7 +144,7 @@ type WireMode = { interactive: boolean; assumeYes: boolean };
|
||||
|
||||
/**
|
||||
* (Re)generate the wmill-managed TypeScript/Deno IDE config so the editor
|
||||
* resolves `$f/`/`$u/` workspace imports against the local script folders.
|
||||
* resolves `/f/`/`/u/` workspace imports against the local script folders.
|
||||
*
|
||||
* Split into a managed base file (always refreshed) and a user-owned file that
|
||||
* references it. We only ever touch files that are ours: the managed file is
|
||||
@@ -211,8 +208,8 @@ async function refreshManagedTsconfig(defaultTs: "bun" | "deno", mode: WireMode)
|
||||
wire: (parsed) => {
|
||||
// TypeScript does NOT merge `compilerOptions.paths` across `extends` — the
|
||||
// nearest config that defines `paths` wins wholesale. So a config with its
|
||||
// own `paths` would shadow the managed `$f/`/`$u/` mappings and the aliases
|
||||
// would silently fail to resolve. We don't touch the user's paths — warn
|
||||
// own `paths` would shadow the managed `/f/`/`/u/` mappings and they would
|
||||
// silently fail to resolve. We don't touch the user's paths — warn
|
||||
// and leave it for them to wire manually.
|
||||
const co = parsed.compilerOptions;
|
||||
const paths =
|
||||
@@ -227,7 +224,7 @@ async function refreshManagedTsconfig(defaultTs: "bun" | "deno", mode: WireMode)
|
||||
) {
|
||||
return (
|
||||
"defines its own `compilerOptions.paths` (TS won't merge ours in via " +
|
||||
'`extends`); add "$f/*": ["./f/*"] and "$u/*": ["./u/*"] to it'
|
||||
'`extends`); add "/f/*": ["./f/*"] and "/u/*": ["./u/*"] to it'
|
||||
);
|
||||
}
|
||||
const ext = parsed.extends;
|
||||
@@ -252,10 +249,10 @@ async function refreshManagedTsconfig(defaultTs: "bun" | "deno", mode: WireMode)
|
||||
}
|
||||
|
||||
async function refreshManagedDenoImportMap(mode: WireMode) {
|
||||
// Import-map prefix keys must end with "/": "$f/" -> "./f/", "$u/" -> "./u/".
|
||||
// Import-map prefix keys must end with "/": "/f/" -> "./f/", "/u/" -> "./u/".
|
||||
const imports: Record<string, string> = {};
|
||||
for (const [alias, dir] of Object.entries(WORKSPACE_IMPORT_ALIASES)) {
|
||||
imports[`${alias}/`] = `./${dir}/`;
|
||||
for (const dir of WORKSPACE_IMPORT_DIRS) {
|
||||
imports[`/${dir}/`] = `./${dir}/`;
|
||||
}
|
||||
|
||||
// Deno import maps only allow `imports`/`scopes`, so no comment header here.
|
||||
@@ -354,7 +351,7 @@ async function ensureUserReferencesManaged(opts: {
|
||||
} catch {
|
||||
log.warn(
|
||||
`${existingName} couldn't be auto-edited (it may contain comments). Add ${opts.hint} ` +
|
||||
`to pick up wmill's recommended settings (incl. $f//$u/ import aliases).`
|
||||
`to pick up wmill's recommended settings (incl. workspace /f/, /u/ import resolution).`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -379,7 +376,7 @@ async function ensureUserReferencesManaged(opts: {
|
||||
if (wired !== true) {
|
||||
log.warn(
|
||||
`${existingName}: ${wired}. Add ${opts.hint} manually to pick up wmill's ` +
|
||||
`recommended settings (incl. $f//$u/ import aliases).`
|
||||
`recommended settings (incl. workspace /f/, /u/ import resolution).`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -399,7 +396,7 @@ async function ensureUserReferencesManaged(opts: {
|
||||
log.info(
|
||||
colors.gray(
|
||||
`Left ${existingName} unchanged — add ${opts.hint} when ready to enable ` +
|
||||
`$f//$u/ import aliases (or re-run \`wmill refresh tsconfig\`).`
|
||||
`workspace /f/, /u/ import resolution (or re-run \`wmill refresh tsconfig\`).`
|
||||
)
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -131,18 +131,12 @@ export const setupTypeAcquisition = (config: ATABootstrapConfig) => {
|
||||
if (depth == 0) {
|
||||
const relativeDeps = depsToGet.filter((f) => isTypescriptRelativePath(f.raw))
|
||||
relativeDeps.forEach(async (f) => {
|
||||
// `$f/`/`$u/` are local-friendly aliases for the absolute workspace paths
|
||||
// `/f/`,`/u/`. Normalize to the absolute form so the fetch URL and the
|
||||
// registered extra-lib path match what the worker resolves (the editor's
|
||||
// `paths` config maps `$f/` -> `/f/` back to this lib).
|
||||
const raw =
|
||||
f.raw.startsWith('$f/') || f.raw.startsWith('$u/') ? '/' + f.raw.slice(1) : f.raw
|
||||
let path = raw.startsWith('/')
|
||||
? raw
|
||||
: '/' + config.scriptPath + (raw.startsWith('../') ? '/../' : '/.') + raw
|
||||
let path = f.raw.startsWith('/')
|
||||
? f.raw
|
||||
: '/' + config.scriptPath + (f.raw.startsWith('../') ? '/../' : '/.') + f.raw
|
||||
let url = config.root + path
|
||||
let localPath = raw
|
||||
if ((raw.startsWith('.') || raw.startsWith('/')) && !raw.endsWith('.ts')) {
|
||||
let localPath = f.raw
|
||||
if ((f.raw.startsWith('.') || f.raw.startsWith('/')) && !f.raw.endsWith('.ts')) {
|
||||
url += '.ts'
|
||||
localPath += '.ts'
|
||||
}
|
||||
|
||||
@@ -100,14 +100,6 @@ export function setMonacoTypescriptOptions() {
|
||||
allowImportingTsExtensions: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
moduleResolution: ModuleResolutionKind.NodeJs,
|
||||
// `$f/`/`$u/` are local-friendly aliases for the absolute workspace paths
|
||||
// `/f/`,`/u/`. ATA registers imported scripts as extra libs under their
|
||||
// absolute `/f/...` path, so map the aliases back so type acquisition resolves.
|
||||
baseUrl: '/',
|
||||
paths: {
|
||||
'$f/*': ['/f/*'],
|
||||
'$u/*': ['/u/*']
|
||||
},
|
||||
jsx: JsxEmit.React
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,15 +11,7 @@ export function parseTypescriptDeps(code: string): string[] {
|
||||
}
|
||||
|
||||
export function isTypescriptRelativePath(d: string) {
|
||||
return (
|
||||
d.startsWith('./') ||
|
||||
d.startsWith('../') ||
|
||||
d.startsWith('/') ||
|
||||
d.startsWith('.../') ||
|
||||
// `$f/`/`$u/` are local-friendly aliases for the absolute workspace paths `/f/`,`/u/`.
|
||||
d.startsWith('$f/') ||
|
||||
d.startsWith('$u/')
|
||||
)
|
||||
return d.startsWith('./') || d.startsWith('../') || d.startsWith('/') || d.startsWith('.../')
|
||||
}
|
||||
|
||||
export function approximateFindPythonRelativePath(code: string) {
|
||||
|
||||
Reference in New Issue
Block a user