Skip to content
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

Bugfix: fix circular dependency loop in dev #3562

Merged
merged 2 commits into from Aug 24, 2021
Merged

Conversation

drwpow
Copy link
Collaborator

@drwpow drwpow commented Jul 8, 2021

Changes

Fixes #3466. If an npm dependency depends on another, we were trying to install both over and over again without checking. This PR memoizes resolved npm packages so each is only hit once.

This should also improve the dev server cold start time by a little bit, as packages are better-memoized now.

Testing

Test added

Docs

Internal change; no docs needed

@changeset-bot
Copy link

changeset-bot bot commented Jul 8, 2021

🦋 Changeset detected

Latest commit: 8137901

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
snowpack Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Jul 8, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/pikapkg/snowpack/6ENtfdTXMyfVGESwUAfaao8GqnPb
✅ Preview: https://snowpack-git-fix-circular-dev-install-pikapkg.vercel.app

const spec = code.substring(imp.s, imp.e).replace(/(\/|\\)+$/, ''); // remove trailing slash from end of specifier (easier for Node to resolve)
if (isRemoteUrl(spec)) {
continue;
const scannedImport = code.substring(imp.s, imp.e).replace(/(\/|\\)+$/, ''); // remove trailing slash from end of specifier (easier for Node to resolve)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor change: don’t shadow spec in parent scope; it’s confusing.

continue;
}
packageImports.add(spec);
const [scannedPackageName] = parsePackageImportSpecifier(scannedImport);
if (scannedPackageName && memoizedImportMap[scannedPackageName]) {
Copy link
Collaborator Author

@drwpow drwpow Jul 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix here: using the in-memory import maps to prevent reinstalling duplicate packages (import map means install happened successfully). Otherwise, we would end up stuck in an infinite loop installing circular npm deps.

await importMapHandle.close();
let existingImportMap: ImportMap | undefined = memoizedImportMap[packageName];
if (!existingImportMap && existsSync(existingImportMapLoc)) {
existingImportMap = JSON.parse(await fs.readFile(existingImportMapLoc, 'utf8'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using existsSync and then readFile still scares me as the previous bug was that the file existed but was not open for writing (because currently being written) which caused it to throw at this line. Even if logic prevents the race we should still use fs.open here as that guarantees both that the file exists and that it's finished being written to.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you have a good point. Will change back, but probably keep the in-memory memoization for already-read files

@hklsiteimprove
Copy link
Contributor

This PR might fix the following bug as well: #3188
@drwpow @matthewp do you believe this could be solved? and if so, is there anything I can do to help or push it through? 👍

@hklsiteimprove
Copy link
Contributor

This PR might fix the following bug as well: #3188
@drwpow @matthewp do you believe this could be solved? and if so, is there anything I can do to help or push it through?

I looked at it and it does seem to solve the bug - at least it does not keep re-resolving the same packages again and again.
I was unable to verify it completely with the local linking, as it had some complications with other packages, but I believe it is good.

Who can help move it forward? :)

@drwpow
Copy link
Collaborator Author

drwpow commented Aug 24, 2021

@hklsiteimprove sorry about this! It fell off my plate. Updated the PR and this should go out in the next release after it merges

* Though this test is slow, it’s important to test on real npm packages and not mocked ones
* as symlink behavior is really different here
*/
describe.skip('@nivo/core', () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the comment said, added a test here which can be run manually. But skipping this in GitHub because for some reason this test keeps flaking in GitHub CI. But I’m able to run it on my machine in Node 12, 14, and 16 without fail every time. So this does fix the issue!

@drwpow drwpow merged commit d72dc7d into main Aug 24, 2021
@drwpow drwpow deleted the fix-circular-dev-install branch August 24, 2021 20:39
@hklsiteimprove
Copy link
Contributor

hklsiteimprove commented Aug 25, 2021

@drwpow This PR fixed the circular resolving really nicely.
It did, however, break the web test runner import for the dev server

Error while running tests:
Error: Cannot find module ''../../../../../__web-dev-server__web-socket.js'' from '/opt/app/node_modules/@web/test-runner-commands/browser/commands.mjs'
    at Function.resolveSync [as sync] (/opt/app/node_modules/resolve/lib/sync.js:102:15)
    at Object.resolveEntrypoint (/opt/app/node_modules/esinstall/lib/entrypoints.js:149:59)
    at PackageSourceLocal.buildPackageImport (/opt/app/node_modules/snowpack/lib/cjs/sources/local.js:424:40)
    at async /opt/app/node_modules/snowpack/lib/cjs/sources/local.js:606:21
    at async PackageSourceLocal.buildPackageImport (/opt/app/node_modules/snowpack/lib/cjs/sources/local.js:454:30)
    at async /opt/app/node_modules/snowpack/lib/cjs/sources/local.js:606:21
    at async PackageSourceLocal.buildPackageImport (/opt/app/node_modules/snowpack/lib/cjs/sources/local.js:454:30)
    at async PackageSourceLocal.prepare (/opt/app/node_modules/snowpack/lib/cjs/sources/local.js:227:13)
    at async Object.startServer (/opt/app/node_modules/snowpack/lib/cjs/commands/dev.js:240:9)
    at async Object.serverStart (/opt/app/node_modules/@snowpack/web-test-runner-plugin/plugin.js:33:16) {
  code: 'MODULE_NOT_FOUND'
}


I can apply a fix inside the resolving of newPackages where I simply opt out of resolving this package, and it works. But it seems like the wrong place, and it should have been handled somewhere else. Any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Using snowpack dev in CSA freezes, fails to start dev server
3 participants