Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

- Paste as JSON.t or ReScript JSX in VSCode. https://github.com/rescript-lang/rescript-vscode/pull/1141

#### :bug: Bug fix

- Pass RESCRIPT_RUNTIME to analysis process. https://github.com/rescript-lang/rescript-vscode/pull/1145

## 1.66.0

#### :bug: Bug fix
Expand Down
36 changes: 1 addition & 35 deletions server/src/bsc-args/rewatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
IncrementallyCompiledFileInfo,
} from "../incrementalCompilation";
import type { projectFiles } from "../projectFiles";
import config from "../config";
import { findRescriptRuntimesInProject } from "../find-runtime";
import { jsonrpcVersion } from "../constants";

export type RewatchCompilerArgs = {
Expand All @@ -20,39 +18,7 @@ export type RewatchCompilerArgs = {
async function getRuntimePath(
entry: IncrementallyCompiledFileInfo,
): Promise<string | null> {
let rescriptRuntime: string | null =
config.extensionConfiguration.runtimePath ?? null;

if (rescriptRuntime !== null) {
if (debug()) {
console.log(
`Using configured runtime path as RESCRIPT_RUNTIME: ${rescriptRuntime}`,
);
}
return rescriptRuntime;
}

const rescriptRuntimes = await findRescriptRuntimesInProject(
entry.project.workspaceRootPath,
);

if (debug()) {
if (rescriptRuntimes.length === 0) {
console.log(
`Did not find @rescript/runtime directory for ${entry.project.workspaceRootPath}`,
);
} else if (rescriptRuntimes.length > 1) {
console.warn(
`Found multiple @rescript/runtime directories, using the first one as RESCRIPT_RUNTIME: ${rescriptRuntimes.join(", ")}`,
);
} else {
console.log(
`Found @rescript/runtime directory: ${rescriptRuntimes.join(", ")}`,
);
}
}

return rescriptRuntimes.at(0) ?? null;
return utils.getRuntimePathFromWorkspaceRoot(entry.project.workspaceRootPath);
}

export async function getRewatchBscArgs(
Expand Down
43 changes: 20 additions & 23 deletions server/src/incrementalCompilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,33 +262,30 @@ function triggerIncrementalCompilationOfFile(
return;
}

const projectRewatchLockfiles = [
...Array.from(workspaceFolders).map((w) =>
path.resolve(w, c.rewatchLockPartialPath),
),
...Array.from(workspaceFolders).map((w) =>
path.resolve(w, c.rescriptLockPartialPath),
),
path.resolve(projectRootPath, c.rewatchLockPartialPath),
path.resolve(projectRootPath, c.rescriptLockPartialPath),
];

let foundRewatchLockfileInProjectRoot = false;
if (projectRewatchLockfiles.some((lockFile) => fs.existsSync(lockFile))) {
foundRewatchLockfileInProjectRoot = true;
} else if (debug()) {
// computeWorkspaceRootPathFromLockfile returns null if lockfile found (local package) or if no parent found
const computedWorkspaceRoot =
utils.computeWorkspaceRootPathFromLockfile(projectRootPath);
// If null, it means either a lockfile was found (local package) or no parent project root exists
// In both cases, we default to projectRootPath
const workspaceRootPath = computedWorkspaceRoot ?? projectRootPath;

// Determine if lockfile was found for debug logging
// If computedWorkspaceRoot is null and projectRootPath is not null, check if parent exists
const foundRewatchLockfileInProjectRoot =
computedWorkspaceRoot == null &&
projectRootPath != null &&
utils.findProjectRootOfFile(projectRootPath, true) != null;

if (foundRewatchLockfileInProjectRoot && debug()) {
console.log(
`Did not find ${projectRewatchLockfiles.join(" or ")} in project root, assuming bsb`,
`Found rewatch/rescript lockfile in project root, treating as local package in workspace`,
);
} else if (!foundRewatchLockfileInProjectRoot && debug()) {
console.log(
`Did not find rewatch/rescript lockfile in project root, assuming bsb`,
);
}

// if we find a rewatch.lock in the project root, it's a compilation of a local package
// in the workspace.
const workspaceRootPath =
projectRootPath && !foundRewatchLockfileInProjectRoot
? utils.findProjectRootOfFile(projectRootPath, true)
: null;

const bscBinaryLocation = project.bscBinaryLocation;
if (bscBinaryLocation == null) {
if (debug())
Expand Down
114 changes: 114 additions & 0 deletions server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import * as lookup from "./lookup";
import { reportError } from "./errorReporter";
import config from "./config";
import { filesDiagnostics, projectsFiles } from "./projectFiles";
import { workspaceFolders } from "./server";
import { rewatchLockPartialPath, rescriptLockPartialPath } from "./constants";
import { findRescriptRuntimesInProject } from "./find-runtime";

let tempFilePrefix = "rescript_format_file_" + process.pid + "_";
let tempFileId = 0;
Expand Down Expand Up @@ -301,6 +304,12 @@ export let runAnalysisAfterSanityCheck = async (
binaryPath = builtinBinaryPath;
}

let runtime: string | undefined = undefined;
if (semver.gt(rescriptVersion as string, "12.0.0-rc.1")) {
const runtimePath = await getRuntimePathFromProjectRoot(projectRootPath);
runtime = runtimePath ?? undefined;
}

let options: childProcess.ExecFileSyncOptions = {
cwd: projectRootPath || undefined,
maxBuffer: Infinity,
Expand All @@ -315,6 +324,7 @@ export let runAnalysisAfterSanityCheck = async (
config.extensionConfiguration.cache?.projectConfig?.enable === true
? "true"
: undefined,
RESCRIPT_RUNTIME: runtime,
},
};

Expand Down Expand Up @@ -372,6 +382,110 @@ export const toCamelCase = (text: string): string => {
.replace(/(\s|-)+/g, "");
};

/**
* Computes the workspace root path from a project root path by checking for rewatch/rescript lockfiles.
* In a monorepo, this finds the parent project root that contains the workspace.
* If a rewatch/rescript lockfile is found in the project root, it's a local package
* in the workspace, so we return null (which will default to projectRootPath).
*/
export function computeWorkspaceRootPathFromLockfile(
projectRootPath: string | null,
): string | null {
if (projectRootPath == null) {
return null;
}

const projectRewatchLockfiles = [
...Array.from(workspaceFolders).map((w) =>
path.resolve(w, rewatchLockPartialPath),
),
...Array.from(workspaceFolders).map((w) =>
path.resolve(w, rescriptLockPartialPath),
),
path.resolve(projectRootPath, rewatchLockPartialPath),
path.resolve(projectRootPath, rescriptLockPartialPath),
];

const foundRewatchLockfileInProjectRoot = projectRewatchLockfiles.some(
(lockFile) => fs.existsSync(lockFile),
);

// if we find a rewatch.lock in the project root, it's a compilation of a local package
// in the workspace.
return !foundRewatchLockfileInProjectRoot
? findProjectRootOfFile(projectRootPath, true)
: null;
}

// Shared cache: key is either workspace root path or project root path
const runtimePathCache = new Map<string, string | null>();

/**
* Gets the runtime path from a workspace root path.
* This function is cached per workspace root path.
*/
export async function getRuntimePathFromWorkspaceRoot(
workspaceRootPath: string,
): Promise<string | null> {
// Check cache first
if (runtimePathCache.has(workspaceRootPath)) {
return runtimePathCache.get(workspaceRootPath)!;
}

// Compute and cache
let rescriptRuntime: string | null =
config.extensionConfiguration.runtimePath ?? null;

if (rescriptRuntime !== null) {
runtimePathCache.set(workspaceRootPath, rescriptRuntime);
return rescriptRuntime;
}

const rescriptRuntimes =
await findRescriptRuntimesInProject(workspaceRootPath);

const result = rescriptRuntimes.at(0) ?? null;
runtimePathCache.set(workspaceRootPath, result);
return result;
}

/**
* Gets the runtime path from a project root path.
* Computes the workspace root path and then resolves the runtime.
* This function is cached per project root path.
*/
export async function getRuntimePathFromProjectRoot(
projectRootPath: string | null,
): Promise<string | null> {
if (projectRootPath == null) {
return null;
}

// Check cache first (keyed by projectRootPath)
if (runtimePathCache.has(projectRootPath)) {
return runtimePathCache.get(projectRootPath)!;
}

// Compute workspace root and resolve runtime
const workspaceRootPath =
computeWorkspaceRootPathFromLockfile(projectRootPath) ?? projectRootPath;

// Check cache again with workspace root (might have been cached from a previous call)
if (runtimePathCache.has(workspaceRootPath)) {
const result = runtimePathCache.get(workspaceRootPath)!;
// Cache it under projectRootPath too for faster lookup next time
runtimePathCache.set(projectRootPath, result);
return result;
}

// Compute and cache
const result = await getRuntimePathFromWorkspaceRoot(workspaceRootPath);
// Cache it under both keys
runtimePathCache.set(workspaceRootPath, result);
runtimePathCache.set(projectRootPath, result);
return result;
}

export const getNamespaceNameFromConfigFile = (
projDir: p.DocumentUri,
): execResult => {
Expand Down