Skip to content

When installing unrelated package inside scoped packages dont invalidate resolutions from everything in the scoped package #53873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 17, 2023
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
10 changes: 5 additions & 5 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1889,24 +1889,24 @@ export function pathContainsNodeModules(path: string): boolean {
*
* @internal
*/
export function parseNodeModuleFromPath(resolved: string): string | undefined {
export function parseNodeModuleFromPath(resolved: string, isFolder?: boolean): string | undefined {
const path = normalizePath(resolved);
const idx = path.lastIndexOf(nodeModulesPathPart);
if (idx === -1) {
return undefined;
}

const indexAfterNodeModules = idx + nodeModulesPathPart.length;
let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterNodeModules);
let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterNodeModules, isFolder);
if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) {
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName, isFolder);
}
return path.slice(0, indexAfterPackageName);
}

function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number, isFolder: boolean | undefined): number {
const nextSeparatorIndex = path.indexOf(directorySeparator, prevSeparatorIndex + 1);
return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
return nextSeparatorIndex === -1 ? isFolder ? path.length : prevSeparatorIndex : nextSeparatorIndex;
}

function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD
// If the invalidated file is from a node_modules package, invalidate everything else
// in the package since we might not get notifications for other files in the package.
// This hardens our logic against unreliable file watchers.
const packagePath = parseNodeModuleFromPath(fileOrDirectoryPath);
const packagePath = parseNodeModuleFromPath(fileOrDirectoryPath, /*isFolder*/ true);
if (packagePath) (startsWithPathChecks ||= new Set()).add(packagePath as Path);
}
}
Expand Down
61 changes: 61 additions & 0 deletions src/testRunner/unittests/tscWatch/resolutionCache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ts from "../../_namespaces/ts";
import * as Utils from "../../_namespaces/Utils";
import { libContent } from "../tsc/helpers";
import {
createWatchedSystem,
File,
Expand Down Expand Up @@ -621,4 +622,64 @@ declare namespace NodeJS {
},
]
});

verifyTscWatch({
scenario,
subScenario: "scoped package installation",
commandLineArgs: ["--w", "-p", `.`, "--traceResolution", "--extendedDiagnostics"],
sys: () => createWatchedSystem({
"/user/username/projects/myproject/lib/app.ts": Utils.dedent`
import { myapp } from "@myapp/ts-types";
const x: 10 = myapp;
`,
"/user/username/projects/myproject/tsconfig.json": "{}",
[libFile.path]: libContent,
}, { currentDirectory: "/user/username/projects/myproject" }),
edits: [
{
caption: "npm install unrelated non scoped",
edit: sys => sys.ensureFileOrFolder({
path: `/user/username/projects/myproject/node_modules/unrelated/index.d.ts`,
content: `export const unrelated = 10;`
}),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "npm install unrelated scoped in myapp",
edit: sys => sys.ensureFileOrFolder({
path: `/user/username/projects/myproject/node_modules/@myapp/unrelated/index.d.ts`,
content: `export const myappUnrelated = 10;`
}),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "npm install unrelated2 scoped in myapp",
edit: sys => sys.ensureFileOrFolder({
path: `/user/username/projects/myproject/node_modules/@myapp/unrelated2/index.d.ts`,
content: `export const myappUnrelated2 = 10;`
}),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
{
caption: "npm install ts-types",
edit: sys => sys.ensureFileOrFolder({
path: `/user/username/projects/myproject/node_modules/@myapp/ts-types/index.d.ts`,
content: `export const myapp = 10;`
}),
timeouts: sys => {
sys.runQueuedTimeoutCallbacks();
sys.runQueuedTimeoutCallbacks();
},
},
]
});
});
Loading