GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — esm: do not call `getSource` when format is `commonjs` by fasttime · Pull Request #50465 · nodejs/node

Conversation

@fasttime

@fasttime fasttime commented Oct 29, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

This PR ensures that defaultLoad does not access the file system to read the source of modules that are known in advance to be in CommonJS format. This is not necessary, since the CommonJS loader can't use the source property and must instead read the file once again.

For context, this was noticed while investigating a test failure in ESLint on Node.js 21.1.0. Our logic (now fixed) was incorrectly assuming that some asynchronous operations would complete in a specific order, and this order has changed from Node.js 21.0.0 to 21.1.0.
The simplest repro I've found is this one:

// index.mjs
await import('./file1.cjs');
process.nextTick(() => console.log('Tick'));
await import('./file2.cjs');
console.log('Done');

with file1.cjs and file2.cjs both empty modules.

This is the output of running index.mjs in Node.js 21.0.0:

Done
Tick

And this is the output in Node.js 21.1.0:

Tick
Done

Here is another similar repro in a single file:

Details
import { mkdtemp, writeFile }   from 'node:fs/promises';
import { join }                 from 'node:path';
import { pathToFileURL }        from 'node:url';

const dir = await mkdtemp('test');
const file1 = join(dir, 'file1.cjs');
const file2 = join(dir, 'file2.cjs');
await Promise.all([writeFile(file1, ''), await writeFile(file2, '')]);

await import(pathToFileURL(file1));
process.nextTick(() => console.log('Tick'));
await import(pathToFileURL(file2));
console.log('Done');

The reason for the inverted order is this commit and a later change, which is causing getSource to be called even when the format of a module is already known to be commonjs, for example because of the .cjs extension.
getSource accesses the file system and that consistently requires one or more I/O cycles to complete. This allows callbacks scheduled with process.nextTick to run in the meantime.

Clearly, importing a CommonJS module is an asynchronous operation which souldn't be assumed to resolve in a specific order or number of phases, but calling getSource for known CommonJS modules isn't needed and should be avoided to not occupy the event loop unnecessarily.

Refs: eslint/eslint#17683

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

bricss reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@nodejs-github-bot

nodejs-github-bot commented Oct 29, 2023

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot nodejs-github-bot added esm Issues and PRs related to the ECMAScript Modules implementation. needs-ci PRs that need a full CI run. labels Oct 29, 2023
@fasttime
fasttime marked this pull request as ready for review October 29, 2023 10:27
@ljharb

ljharb commented Oct 29, 2023

Copy link
Copy Markdown
Member

If the observable output is changing then that’s a breaking change, so it’s pretty important to fix.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

aduh95
aduh95 reviewed Oct 29, 2023
View reviewed changes

@aduh95 aduh95 left a comment

Copy link
Copy Markdown
Contributor

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.

Thanks! A few comments regarding the tests:

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread test/es-module/test-esm-dynamic-import-commonjs.js Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread test/es-module/test-esm-dynamic-import-commonjs.js Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread test/es-module/test-esm-dynamic-import-commonjs.mjs Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread test/es-module/test-esm-dynamic-import-commonjs.mjs Resolved

Uh oh!

There was an error while loading. Please reload this page.

jasnell
jasnell approved these changes Oct 29, 2023
View reviewed changes
@GeoffreyBooth

GeoffreyBooth commented Oct 30, 2023

Copy link
Copy Markdown
Member

I feel like the bug will just resurface whenever custom hooks are present and returning a source for a CommonJS file. At the very least this PR should add a test to prove that my worry is unfounded; but perhaps a better solution is to fetch the source within the ESM loader like we do for all other module types and the CJS loader somehow uses the already-retrieved source rather than attempt to load it again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@fasttime

fasttime commented Nov 1, 2023

Copy link
Copy Markdown
Contributor Author

perhaps a better solution is to fetch the source within the ESM loader like we do for all other module types and the CJS loader somehow uses the already-retrieved source rather than attempt to load it again.

Thanks @GeoffreyBooth, I've created a new branch with the change you suggested. I had to use a new internal property realSource to store the source fetched by the ESM loader in the result. If this property is set by a custom loader, it will be discarded along with other unknown properties, meaning that the public API isn't altered.

I like this change because it replaces a synchronous read operation with an asynchronous one. The downside is that there would be a discrepancy between defaultLoad and (existing) custom loader implementations. There seems to be also a concern that the observable behavior [in versions prior to 21.1.0] shouldn't change, so I can't decide which solution is better.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Nov 1, 2023

Copy link
Copy Markdown
Contributor

We cannot use the source from the ESM loader, it would short cut all the monkey patching of the CJS loader.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@fasttime

fasttime commented Nov 1, 2023

Copy link
Copy Markdown
Contributor Author

We cannot use the source from the ESM loader, it would short cut all the monkey patching of the CJS loader.

It doesn't if source stays null, that's why I had to use a new property.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Nov 1, 2023

Copy link
Copy Markdown
Contributor

I think the actual fix would be to ask the main thread CJS loader for the source rather than calling getSource from the loader thread.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Nov 1, 2023

Copy link
Copy Markdown
Contributor

That's a bummer because we are looking for ways to remove dependency on the monkey patchable CJS loader, but oh well. Or we accept the breaking change, and ask folks who rely on the monkey patchability of the CJS loader to switch to the new API (which mean we can't unflag until the ecosystem is ready)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@GeoffreyBooth

GeoffreyBooth commented Nov 1, 2023

Copy link
Copy Markdown
Member

ask folks who rely on the monkey patchability of the CJS loader

Because monkey patching is unsupported, there's no defined API surface. We don't really know what would or wouldn't break people.

In this case, I think the only impact would be people who monkey patch the CJS loader in order to affect import of CJS? Which is no one? I think we should just refactor as is best for our codebase and not worry about theoretical breaks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Nov 1, 2023

Copy link
Copy Markdown
Contributor

I think the only impact would be people who monkey patch the CJS loader in order to affect import of CJS? Which is no one?

Which is everyone you mean? I think Yarn PnP relies on that for example, or any project that uses pirates hooks I think.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@GeoffreyBooth

GeoffreyBooth commented Nov 1, 2023

Copy link
Copy Markdown
Member

No, that's relying on monkey patching to affect require, not import.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Nov 1, 2023

Copy link
Copy Markdown
Contributor

No, that's relying on monkey patching to affect require, not import.

I don’t know what makes you think that, I think that’s not correct.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@GeoffreyBooth

GeoffreyBooth commented Nov 1, 2023

Copy link
Copy Markdown
Member

I don’t know what makes you think that, I think that’s not correct.

Can you provide an example where someone is monkey-patching the CommonJS loader to affect import of CommonJS?

Regardless, I don’t think it’s a supported use case. I don’t think monkey-patching at all is a supported use case, and monkey-patching the CommonJS loader to affect only CommonJS files in a mixed CommonJS-ESM codebase is even less supported: we provide the module hooks for that.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95

aduh95 commented Nov 1, 2023

Copy link
Copy Markdown
Contributor

Can you provide an example where someone is monkey-patching the CommonJS loader to affect import of CommonJS?

Do you mean examples other than the one I gave in #50465 (comment)? Could you clarify why you think those are not good examples?

we provide the module hooks for that

not really, if you provide a source with module hooks for commonjs, you have to use faux CJS, which not everyone can switch to overnight (e.g. a large portion of the ecosystem relies on require.cache to be a thing).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ljharb

ljharb commented Nov 1, 2023

Copy link
Copy Markdown
Member

(nb, also require.extensions)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@fasttime

fasttime commented Nov 6, 2023

Copy link
Copy Markdown
Contributor Author

Since this PR is all about avoiding to read the same file twice with the ESM loader, maybe it would be better to continue the discussion about the CommonJS monkey-patching legacy in a separate issue to give it more visibility and solicit other perspectives. It looks like there is more need to speak about that.

Meanwhile, I'm happy to update and extend the unit tests as suggested so we can move on with this issue.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@fasttime

fasttime commented Nov 7, 2023

Copy link
Copy Markdown
Contributor Author

I feel like the bug will just resurface whenever custom hooks are present and returning a source for a CommonJS file. At the very least this PR should add a test to prove that my worry is unfounded

@GeoffreyBooth I've added a unit test to ensure that the file specified in the URL is not used when a source is already present. I'm saying used because the actual read access is wrapped in a try-catch statement, so even if a read attempt occurred, it would not be possible to detect that situation by simulating a read error. Still, the ??= operator guarantees that the when fs.readFile is called, the result is stored in source, so checking the source should be enough to exclude a read operation. Let me know if you have a better idea. Here is the new test: c93377f. Please, have a look again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@fasttime
fasttime requested a review from aduh95 November 7, 2023 13:14
aduh95
aduh95 approved these changes Nov 7, 2023
View reviewed changes
Comment thread test/es-module/test-esm-loader-with-source.mjs Outdated

@aduh95 aduh95 Nov 7, 2023

Copy link
Copy Markdown
Contributor

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.

nit: since this does not in fact reference a fixtures, let's simplify.

Suggested change
const { default: noSuchFileSource } = await import(fixtures.fileURL('no-such-file.cjs'));
const { default: noSuchFileSource } = await import('./no-such-file.cjs');

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@fasttime fasttime Nov 8, 2023

Copy link
Copy Markdown
Contributor Author

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.

Thanks! Unfortunately, './no-such-file.cjs' wouldn't work because it's not a valid URL, and the resolve hook doesn't actually resolve relative paths because of the short-circuit. Anyway, a file URL like 'file:///no-such-file.cjs' works fine and is not relative (at least on Unix), so I switched to use that.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Nov 8, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

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 think it's incorrect to say it's not a valid URL, it is a valid (relative) URL.

Anyway, a file URL like 'file:///no-such-file.cjs' works fine

IMO it would make more sense to use a relative URL in the JS, and have the hook return a specific URL. That doesn't really matter anyway, for the purpose of this test the current version works, so good enough for me.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@fasttime fasttime Nov 8, 2023

Copy link
Copy Markdown
Contributor Author

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.

Never mind, I had to change the test again because Windows doesn't like the absolute file URL without a drive letter: 4dcbec5. Let's see how the CI build goes this time.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@aduh95 aduh95 added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Nov 8, 2023
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Nov 8, 2023
@nodejs-github-bot

nodejs-github-bot commented Nov 8, 2023

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/55501/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Nov 9, 2023
CI Reliability 2023-11-09 nodejs/reliability#712
Open
CI Reliability 2023-11-10 nodejs/reliability#713
Open
@aduh95 aduh95 added the commit-queue-squash PRs the Commit Queue should land as one squashed commit. label Nov 29, 2023
@nodejs-github-bot nodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Nov 29, 2023
@nodejs-github-bot
nodejs-github-bot merged commit 2602c46 into nodejs:main Nov 29, 2023
@nodejs-github-bot

nodejs-github-bot commented Nov 29, 2023

Copy link
Copy Markdown
Collaborator

Landed in 2602c46

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

RafaelGSS pushed a commit that referenced this pull request Nov 29, 2023
@fasttime @RafaelGSS
esm: do not call getSource when format is commonjs
bd58870
Ensure that `defaultLoad` does not uselessly access the file system to
get the source of modules that are known to be in CommonJS format.

This allows CommonJS imports to resolve in the current phase of the
event loop.

Refs: eslint/eslint#17683
PR-URL: #50465
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
@RafaelGSS RafaelGSS mentioned this pull request Nov 29, 2023
v21.3.0 proposal #50954
Merged
UlisesGascon pushed a commit that referenced this pull request Dec 11, 2023
@fasttime @UlisesGascon
esm: do not call getSource when format is commonjs
8feb2db
Ensure that `defaultLoad` does not uselessly access the file system to
get the source of modules that are known to be in CommonJS format.

This allows CommonJS imports to resolve in the current phase of the
event loop.

Refs: eslint/eslint#17683
PR-URL: #50465
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
@UlisesGascon UlisesGascon mentioned this pull request Dec 12, 2023
v20.11.0 proposal #51124
Merged
UlisesGascon pushed a commit that referenced this pull request Dec 13, 2023
@fasttime @UlisesGascon
esm: do not call getSource when format is commonjs
7c386fa
Ensure that `defaultLoad` does not uselessly access the file system to
get the source of modules that are known to be in CommonJS format.

This allows CommonJS imports to resolve in the current phase of the
event loop.

Refs: eslint/eslint#17683
PR-URL: #50465
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
UlisesGascon pushed a commit that referenced this pull request Dec 15, 2023
@fasttime @UlisesGascon
esm: do not call getSource when format is commonjs
2239d66
Ensure that `defaultLoad` does not uselessly access the file system to
get the source of modules that are known to be in CommonJS format.

This allows CommonJS imports to resolve in the current phase of the
event loop.

Refs: eslint/eslint#17683
PR-URL: #50465
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
UlisesGascon pushed a commit that referenced this pull request Dec 19, 2023
@fasttime @UlisesGascon
esm: do not call getSource when format is commonjs
8d5469c
Ensure that `defaultLoad` does not uselessly access the file system to
get the source of modules that are known to be in CommonJS format.

This allows CommonJS imports to resolve in the current phase of the
event loop.

Refs: eslint/eslint#17683
PR-URL: #50465
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
codebytere added a commit to electron/electron that referenced this pull request Jan 10, 2024
@codebytere
esm: do not call getSource when format is commonjs
b9e5303
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 10, 2024
@codebytere
esm: do not call getSource when format is commonjs
4472819
nodejs/node#50465
zcbenz pushed a commit to electron/electron that referenced this pull request Jan 12, 2024
@codebytere @zcbenz
esm: do not call getSource when format is commonjs
b538ef4
nodejs/node#50465
zcbenz pushed a commit to electron/electron that referenced this pull request Jan 12, 2024
@codebytere @zcbenz
esm: do not call getSource when format is commonjs
1ccffa5
nodejs/node#50465
zcbenz pushed a commit to electron/electron that referenced this pull request Jan 12, 2024
@codebytere @zcbenz
esm: do not call getSource when format is commonjs
585c4d0
nodejs/node#50465
zcbenz pushed a commit to electron/electron that referenced this pull request Jan 12, 2024
@codebytere @zcbenz
esm: do not call getSource when format is commonjs
f7d5537
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 15, 2024
@codebytere
esm: do not call getSource when format is commonjs
1417e3b
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 15, 2024
@codebytere
esm: do not call getSource when format is commonjs
5c96ca9
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 15, 2024
@codebytere
esm: do not call getSource when format is commonjs
ef46db8
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 15, 2024
@codebytere
esm: do not call getSource when format is commonjs
86a18fe
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 16, 2024
@codebytere
esm: do not call getSource when format is commonjs
e190b35
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 16, 2024
@codebytere
esm: do not call getSource when format is commonjs
cbb3631
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 18, 2024
@codebytere
esm: do not call getSource when format is commonjs
460ee2c
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 18, 2024
@codebytere
esm: do not call getSource when format is commonjs
7bbad41
nodejs/node#50465
jkleinsc pushed a commit to electron/electron that referenced this pull request Jan 18, 2024
@codebytere @jkleinsc
esm: do not call getSource when format is commonjs
847b034
nodejs/node#50465
jkleinsc pushed a commit to electron/electron that referenced this pull request Jan 18, 2024
@codebytere @jkleinsc
esm: do not call getSource when format is commonjs
b3bfca7
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 18, 2024
@codebytere
esm: do not call getSource when format is commonjs
6752888
nodejs/node#50465
codebytere added a commit to electron/electron that referenced this pull request Jan 18, 2024
@codebytere
esm: do not call getSource when format is commonjs
cda0cb4
nodejs/node#50465
jkleinsc pushed a commit to electron/electron that referenced this pull request Jan 18, 2024
@electron-roller @codebytere @zcbenz
chore: bump node to v20.11.0 (main) (#40941)
f4ee3c1
* chore: bump node in DEPS to v20.11.0

* module: bootstrap module loaders in shadow realm

nodejs/node#48655

* src: add commit hash shorthand in zlib version

nodejs/node#50158

* v8,tools: expose necessary V8 defines

nodejs/node#50820

* esm: do not call getSource when format is commonjs

nodejs/node#50465

* esm: fallback to readFileSync when source is nullish

nodejs/node#50825

* vm: allow dynamic import with a referrer realm

nodejs/node#50360

* test: skip test-diagnostics-channel-memory-leak.js

nodejs/node#50327

* esm: do not call getSource when format is commonjs

nodejs/node#50465

* lib: fix assert throwing different error messages in ESM and CJS

nodejs/node#50634

* src: fix compatility with upcoming V8 12.1 APIs

nodejs/node#50709

* deps: update base64 to 0.5.1

nodejs/node#50629

* src: avoid silent coercion to signed/unsigned int

nodejs/node#50663

* src: fix compatility with upcoming V8 12.1 APIs

nodejs/node#50709

* chore: fix patch indices

* chore: update patches

* test: disable TLS cipher test

This can't be enabled owing to BoringSSL incompatibilities.

nodejs/node#50186

* fix: check for Buffer and global definition in shadow realm

nodejs/node#51239

* test: disable parallel/test-shadow-realm-custom-loader

Incompatible with our asar logic, resulting in the following failure:

> Failed to CompileAndCall electron script: electron/js2c/asar_bundle

* chore: remove deleted parallel/test-crypto-modp1-error test

* test: make test-node-output-v8-warning generic

nodejs/node#50421

* chore: fixup ModuleWrap patch

* test: match wpt/streams/transferable/transform-stream-members.any.js to upstream

* fix: sandbox is not enabled on arm

* chore: disable v8 sandbox on ia32/arm

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <[email protected]>
Co-authored-by: Cheng Zhao <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@jasnell jasnell jasnell approved these changes
@aduh95 aduh95 aduh95 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. commit-queue-squash PRs the Commit Queue should land as one squashed commit. esm Issues and PRs related to the ECMAScript Modules implementation. needs-ci PRs that need a full CI run.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

6 participants

@fasttime @nodejs-github-bot @ljharb @GeoffreyBooth @aduh95 @jasnell
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.