-
Couldn't load subscription status.
- Fork 724
Fix #1034: Improve symlink resolution in module specifier generation #1902
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
base: main
Are you sure you want to change the base?
Conversation
|
My intention is to help fix #1034, which is the only thing left blocking my team from adopting tsgo. I'm not very experienced with Go, but I wanted to expand upon @shinichy's work here: https://github.com/shinichy/typescript-go/tree/symlinks |
|
We have the same issue in our team that's stopping us from adopting So I compiled your branch and ran a test, it doesn't seem to make any difference in our code base. Just wanted to let you know. |
Extends the symlink support in GetEachFileNameOfModule to properly resolve module specifiers across symlinked packages and workspaces. Key changes: - Move knownsymlinks from compiler to dedicated symlinks package - Implement active resolution via ResolveModuleName to populate cache - Add dependency resolution from package.json to detect symlinks early - Improve ignored path handling (node_modules/., .git, .# emacs locks) - Add comprehensive test coverage for symlink resolution - Fix declaration emit to prefer original paths over symlink paths This aligns with upstream TypeScript's symlink resolution behavior, ensuring correct module specifiers in declaration files for monorepos and symlinked dependencies. Fixes baseline mismatches in: - declarationEmitReexportedSymlinkReference2/3 - symlinkedWorkspaceDependencies* tests - nodeModuleReexportFromDottedPath
Optimizes populateSymlinkCacheFromResolutions to avoid redundant dependency resolution. Previously, every module specifier generation would re-resolve all package.json dependencies. Now uses package-level caching to resolve once and reuse results. Performance improvements (measured with benchmarks): - Speed: 9.28x faster (89.2% reduction: 509µs → 55µs per operation) - Memory: 8.64x less (88.4% reduction: 597KB → 69KB) - Allocations: 9.22x fewer (89.2% reduction: 12,177 → 1,321) Key changes: - Add package-level cache tracking in KnownSymlinks - Eliminate intermediate slice allocations - Reduce redundant ToPath() calls - Add comprehensive benchmarks for symlink operations For a project with 50 dependencies and 100 files, this saves multiple seconds of compilation time by avoiding 5,000+ redundant resolutions.
Interesting. This is what I get in Linux after setting up your repo and replacing Seems like this might need some fixes for macOS. |
|
@neo773 should be fixed with the latest commit, the above errors seem like they might be actual errors or at least ones that can be fixed trivially: Patch
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-text-extractor.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-text-extractor.service.ts
index f81f151..335fe77 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-text-extractor.service.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-text-extractor.service.ts
@@ -8,10 +8,12 @@ import * as planer from 'planer';
import { safeDecodeURIComponent } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/safe-decode-uri-component.util';
+type DOMPurifyInstance = ReturnType<typeof DOMPurify>;
+
@Injectable()
export class ImapMessageTextExtractorService {
private readonly jsdomInstance: JSDOM;
- private readonly purify: DOMPurify.DOMPurify;
+ private readonly purify: DOMPurifyInstance;
constructor() {
this.jsdomInstance = new JSDOM('');
diff --git a/packages/twenty-server/src/utils/image.ts b/packages/twenty-server/src/utils/image.ts
index 8e98729..ad78167 100644
--- a/packages/twenty-server/src/utils/image.ts
+++ b/packages/twenty-server/src/utils/image.ts
@@ -1,4 +1,4 @@
-import { type Axios } from 'axios';
+import { type AxiosInstance } from 'axios';
const cropRegex = /([w|h])([0-9]+)/;
@@ -24,7 +24,7 @@ export const getCropSize = (value: ShortCropSize): CropSize | null => {
export const getImageBufferFromUrl = async (
url: string,
- axiosInstance: Axios,
+ axiosInstance: AxiosInstance,
): Promise<Buffer> => {
const response = await axiosInstance.get(url, {
responseType: 'arraybuffer',
diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/failing-field-metadata-morph-relation-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/failing-field-metadata-morph-relation-creation.integration-spec.ts
index 0aa4bb7..2981c77 100644
--- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/failing-field-metadata-morph-relation-creation.integration-spec.ts
+++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/failing-field-metadata-morph-relation-creation.integration-spec.ts
@@ -1,4 +1,4 @@
-import { faker } from '@faker-js/faker/.';
+import { faker } from '@faker-js/faker';
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; |
|
@chase Thank you for your work on this. |
|
Thanks for working on this! Fixes #1034 (comment) and #1657 also |
| // if p.Host().GetSymlinkCache() != nil { | ||
| // return p.Host().GetSymlinkCache() | ||
| // } | ||
| if p.knownSymlinks == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition looks to be impossible as-written, but I think lazy initialization is a good idea. However, you need to guard the field initialization with a sync.Once like the other lazy computed caches.
| // In declaration-only builds, the symlink cache might not be populated yet | ||
| // because module resolution was skipped. Populate it now if we have resolutions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t recall exactly how this happened in Strada, but I don’t think this comment applies in Corsa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This occurs regularly in pnpm workspaces when running tsgo --build --emitDeclarationsOnly, if I recall correctly.
| // Helper to resolve dependencies without creating intermediate slices | ||
| resolveDeps := func(deps map[string]string) { | ||
| for depName := range deps { | ||
| resolved := host.ResolveModuleName(depName, packageJsonPath, options.OverrideImportMode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d like to get this out of here by frontloading these resolutions to program construction. That will allow us to remove ResolveModuleName from the host interfaces and should also remove all concurrent writes from the symlink cache. In moving it there, I also think the program could do a better job of determining which of these resolutions are necessary and avoid doing redundant ones. The original comment that said most of these resolutions will already be cached is not currently true in Corsa.
It is likely the case that we get 90% of the way there without this block of logic, as this was added to Strada long after the symlink cache existed with only the resolutions it made during program construction. Dropping it from this PR and leaving it as a to-do would be fine. The priority is to eliminate file system reads and cache mutations post-program-load.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried that since the current test suite don't adequately cover handling pnpm workspaces, that removing this and relocating some of this willl cause a regression for some of the cases that were solved for others.
I might have time to work on this over the weekend, but if your or another wants to pick up this branch and get it up to standard, I don't want to to block getting a fix merged.
Extends the symlink support in GetEachFileNameOfModule to properly resolve module specifiers across symlinked packages and workspaces.
Key changes:
Fixes #1657 and #1034 (comment)
Fixes #1347