getEditsForFileRename: Support directory rename#24305
Conversation
| if (!program.getSourceFileFromReference(sourceFile, ref) && resolveTripleslashReference(ref.fileName, sourceFile.fileName) === oldFilePath) { | ||
| result.push({ sourceFile, toUpdate: ref }); | ||
| } | ||
| if (program.getSourceFileFromReference(sourceFile, ref)) continue; |
There was a problem hiding this comment.
why would you want to skip if ref was resolved to some sourceFile?
if you had
index.ts:
/// <ref path="./file1.ts"/>file1.ts
// Some contentYou can rename file1.ts to file2.ts and want to change ref in index to file2.ts?
There was a problem hiding this comment.
You're right, I did a similar thing in #24328 but I'll have to update it in this PR too.
| const rel = getRelativePathFromFile(oldFileOrDirPath, newFileOrDirPath, createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host))); | ||
| return (fullPath, relPath, isImport) => { | ||
| if (fullPath === oldFileOrDirPath) { | ||
| const newRelPath = pathIsRelative(relPath) |
There was a problem hiding this comment.
Nit: Pls rename relPath to say textInFile or something like that since it could be non relatvie?
| : program.getResolvedModuleWithFailedLookupLocationsFromCache(importStringLiteral.text, sourceFile.fileName); | ||
| if (resolved && contains(resolved.failedLookupLocations, oldFilePath)) { | ||
| result.push({ sourceFile, toUpdate: importStringLiteral }); | ||
| const updated = resolved && firstDefined(resolved.resolvedModule ? [resolved.resolvedModule.resolvedFileName] : resolved.failedLookupLocations, path => pathUpdater(path, importStringLiteral.text, /*isImport*/ true)); |
There was a problem hiding this comment.
I think you want to check failed lookup locations irrespective of if the resolvedFileName is present because those failedLookups take precedence over whatever was resolved ?
There was a problem hiding this comment.
If it resolved successfully:
- To something not in the old directory: We should leave it alone, right?
- To something in the old directory: Then we'll recognize that at
resolvedFileNameand we're done with no need to continue on tofailedLookupLocations, right?
Can you think of a case where this isn't sufficient?
There was a problem hiding this comment.
Even if it resolved to something not in oldDirectory, if it failed on something in old directory thats going to change the resolution no?
There was a problem hiding this comment.
Ok ignore previous (i got confused between invalidation of resolution and change in name of the resolution), why would you want to change name of import if the changes are happening in file that's present in failed lookup locations of resolution
There was a problem hiding this comment.
That's relevant if the rename has already happened and we've already updated the program. In that case there will be lots of unresolved imports that are trying to import from oldFile, but since that doesn't exist their resolution will fail.
So we have to look through their failed lookup locations to see if they could have resolved to oldFile if it still existed.
Note that means that we should only look at failed lookup locations if the import wasn't already successfully resolved.
There was a problem hiding this comment.
When I have this structure:
Folders b and c are in a
There is node_modules in b where resolution in file a/b/file1.ts has failed lookup locations from a/b/node_modules/ because of say import {a} from "foo" You dont want to rename "foo" to something else right when renaming a/b to a/d ?
26c5d7b to
2f8edbd
Compare
| // e.g., Importing from "/old/a/b", suffix is "/a/b", and we'll leave that part alone. | ||
| const relToOld = removeDirectoryPrefixFromPath(oldFileOrDirPath, fullPath); | ||
| if (relToOld === undefined) return undefined; | ||
| const suffix = isImport ? pathToImportPath(relToOld) : relToOld; |
There was a problem hiding this comment.
moving folder /a/b/ to folder say /a/c would need to move "resolvedFile name" of` import "foo" say ```/a/b/node_modules/foo/index.d.ts``` will make suffix as : ```"node_modules/foo"``` ? anmd newPrefix as ```"/a/c"``` and resulting import would be````"/a/c/node_modules/foo"``` ?
There was a problem hiding this comment.
This function will only be called for imports from outside the moved directory, which won't resolve to node_modules in the moved directory unless it was an explicit relative import from "./b/node_modules/foo".
|
Note: filed #24384 for handling additional tsconfig settings. |
| return (sourceFile, importText) => | ||
| !pathIsRelative(importText) || | ||
| importIsInternalToDirectory(oldFileOrDirPath, sourceFile.fileName, importText) || | ||
| importIsInternalToDirectory(newFileOrDirPath, sourceFile.fileName, importText) |
There was a problem hiding this comment.
If import text is relative and it was relative path resolving in newDirectory, you need to change the text right?
"/a/b/" to "/a/c" and my import text was "in/a/b/file1" was referring to "../c/file3" you want to change it to "./file3" ?
Would it be better to use getRelativePathToDirectoryOrUrl with file and reference new locations instead ?
There was a problem hiding this comment.
If /a/b is being renamed to /a/c, /a/c must not already exist -- so an import to ../c/file3 would have been an error before, right?
There was a problem hiding this comment.
I think I see what you mean in the file case, see latest commit.
|
Please port to release-2.9 as well. |
| if (fullPath === oldFileOrDirPath) { | ||
| const newImportText = pathIsRelative(importText) | ||
| ? combinePathsSafe(tryRemoveIndexOrPackage(fullPath) && !endsWithSomeIndex(importText) ? importText : getDirectoryPath(importText), rel) | ||
| : newFileOrDirPath; |
There was a problem hiding this comment.
With amd resolution, renaming /a/folder/oldModule.ts to /a/folder/newModule.ts and if I had file as
// /a/folder/someApp.ts
import { a } from "oldModule"to change to
// /a/folder/someApp.ts
import { a } from "/a/folder/newModule"| } | ||
|
|
||
| function importIsInternalToDirectory(oldDir: string, sourceFilePath: string, importText: string): boolean { | ||
| return !!removeDirectoryPrefixFromPath(oldDir, combineNormal(getDirectoryPath(sourceFilePath), importText)); |
There was a problem hiding this comment.
For all these operations you want to take into account the case sensitivity as well (since you are using fileName instead of path)
|
Latest commit fixes #24385 |
…h instead of updating relative paths
4f727db to
27f71fa
Compare
|
Latest commit fixes #24384 |
8b39db7 to
ccdd465
Compare
|
Also fixes #24493 |
| const configFile = program.getCompilerOptions().configFile; | ||
| /** If 'path' refers to an old directory, returns path in the new directory. */ | ||
| type PathUpdater = (path: string) => string | undefined; | ||
| function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string): PathUpdater { |
There was a problem hiding this comment.
This also needs to consider case-sensitive vs case-insensitive
| if (!isPropertyAssignment(property) || !isStringLiteral(property.name)) continue; | ||
| switch (property.name.text) { | ||
| case "files": | ||
| case "include": |
There was a problem hiding this comment.
include could be "src/*.ts" and your old path will be oldFileOrDirPath will be full Path correct? (eg. /user/username/project/src). Don't you want to handle that?
There was a problem hiding this comment.
If you are renaming file from "/projects/src/file1.ts" to "/projects/lib/file1.ts" and include is "src/*.ts" do you want to add "lib/file1.ts" in the include section ?
| case "baseUrl": | ||
| case "typeRoots": | ||
| case "mapRoot": | ||
| case "rootDir": |
There was a problem hiding this comment.
PathMapping needs to be updated too ?
249888a to
bc75e67
Compare
| case "typeRoots": | ||
| case "mapRoot": | ||
| case "rootDir": | ||
| case "rootDirs": |
There was a problem hiding this comment.
except files, includes and exclude rest of these are in "compilerOptions" property. So you will miss those?
| function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName): PathUpdater { | ||
| const canonicalOldPath = getCanonicalFileName(oldFileOrDirPath); | ||
| return path => { | ||
| const canonicalPath = getCanonicalFileName(path); |
There was a problem hiding this comment.
This still handles the path as is but you need to be able to handle include/exclude/file like:
"files": "src/foo.ts" or "./src/foo.ts", "../src/foo.ts" etc?
There was a problem hiding this comment.
For include/exclude we don't just call this directly, see tryUpdateString. First we prepend the config directory to the path, call oldToNew, then convert to a relative path again.
| case "mapRoot": | ||
| case "rootDir": | ||
| case "rootDirs": | ||
| updatePaths(property); |
There was a problem hiding this comment.
I think you want to use isFilePath from getOptionNameMap.get(propertyName) to update the paths? instead of listing all these here?
sheetalkamat
left a comment
There was a problem hiding this comment.
Except suggestion to use getOptionNameMap().get().isFilePath changes look good.
|
@Andy-MS please port to release-2.9 as well. |
7457ef9 to
2a73698
Compare
* getEditsForFileRename: Support directory rename * Code review * Handle imports inside the new file/directory * Document path updaters * Shorten relative paths where possible * Reduce duplicate code * Rewrite, use moduleSpecifiers.ts to get module specifiers from scratch instead of updating relative paths * Update additional tsconfig.json fields * Add test with '.js' extension * Handle case-insensitive paths * Better tsconfig handling * Handle properties inside compilerOptions * Use getOptionFromName
* getEditsForFileRename: Support directory rename * Code review * Handle imports inside the new file/directory * Document path updaters * Shorten relative paths where possible * Reduce duplicate code * Rewrite, use moduleSpecifiers.ts to get module specifiers from scratch instead of updating relative paths * Update additional tsconfig.json fields * Add test with '.js' extension * Handle case-insensitive paths * Better tsconfig handling * Handle properties inside compilerOptions * Use getOptionFromName
Fixes #24260