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

Deduplicate protocol.ts content #57361

Merged
merged 9 commits into from
Mar 4, 2024

Conversation

andrewbranch
Copy link
Member

Fixes #57232 and improves some quality of life, hopefully

andrewbranch and others added 3 commits February 9, 2024 12:20
Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Feb 9, 2024
@typescript-bot
Copy link
Collaborator

Thanks for the PR! It looks like you've changed the TSServer protocol in some way. Please ensure that any changes here don't break consumers of the current TSServer API. For some extra review, we'll ping @sheetalkamat, @mjbvz, @zkat, and @joj for you. Feel free to loop in other consumers/maintainers if necessary.

/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/
export interface ApplicableRefactorInfo {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is an example of an interface that was copied exactly from services/types.ts. This can simply be imported instead of duplicated.

*/
kind: OutliningSpanKind;
}
export type OutliningSpan = ChangePropertyTypes<ts.OutliningSpan, { textSpan: TextSpan; hintSpan: TextSpan; }>;
Copy link
Member Author

Choose a reason for hiding this comment

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

This is an example of an interface that was copied from services/types.ts with a couple changed property types. I added a utility for this purpose. I only used it in cases where the majority of properties were identical; when there were many changes, I let the near-duplicate stand. Note that this is a homomorphic mapped type so JSDoc from the source type’s properties is retained.

Comment on lines +25 to +30
import {
ClassificationType,
CompletionTriggerKind,
OrganizeImportsMode,
SemicolonPreference,
} from "./_namespaces/ts";
Copy link
Member Author

Choose a reason for hiding this comment

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

These were identical copies of const enums previously duplicated in this file, which can simply be imported now. Since we have preserveConstEnums on, they have to be imported without import type and re-exported below to ensure the shape of this module doesn’t change. (I validated this manually by comparing Object.keys(ts.server.protocol) before and after this PR.)

Comment on lines -3741 to +3095
ReactNative = "ReactNative",
React = "React",
None = "none",
Preserve = "preserve",
ReactNative = "react-native",
React = "react",
ReactJSX = "react-jsx",
ReactJSXDev = "react-jsxdev",
Copy link
Member Author

Choose a reason for hiding this comment

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

All of these compiler options enums were missing values, but this one also had a value ReactNative = "ReactNative" which has forever been broken, as far as I can tell. When inferred or external compiler options are sent over the wire, enum-typed properties are allowed to be, for example, ts.JsxEmit | protocol.JsxEmit; i.e. a number or a corresponding string value. However, the conversion method simply uses the same parser as command-line or tsconfig values, which means these string values need to match the ones in the maps defined in commandLineParser.ts, which are kebab-cased. Uppercase/lowercase letters don’t matter, but "ReactNative" would not have worked since it was missing the hyphen.

I changed all of these enum values to lowercase for consistency, but react-native was the only one that strictly matters. This is technically a type-level breaking change.

Comment on lines +3149 to +3157
type AssertKeysComplete<Source extends { [K in keyof Target]: any; }, Target> = Source;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type CopiedTypesComplete = [
AssertKeysComplete<typeof ModuleResolutionKind, typeof ts.ModuleResolutionKind>,
AssertKeysComplete<typeof ModuleKind, typeof ts.ModuleKind>,
AssertKeysComplete<typeof ScriptTarget, typeof ts.ScriptTarget>,
AssertKeysComplete<typeof JsxEmit, typeof ts.JsxEmit>,
AssertKeysComplete<typeof IndentStyle, typeof ts.IndentStyle>,
];
Copy link
Member Author

Choose a reason for hiding this comment

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

As mentioned before, what really matters is that the stringified enums represent every value for each option that the command line parser can recognize. Those strings aren’t encoded in types anywhere else that I know of, so rather than checking that directly, these checks just make sure each enum has all the keys the source enum has, protecting us from forgetting to update the protocol when compiler options get new values. The enum values here in the protocol could still be typo’d, as was the case for "ReactNative"

@typescript-bot
Copy link
Collaborator

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

src/server/protocol.ts Outdated Show resolved Hide resolved
Comment on lines 325 to 326
// We didn't find the symbol in scope at all. Just allow it and we'll fail at test time.
return node;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe not for this PR, but I do feel a bit like I should have originally just made this error.

@@ -310,6 +318,10 @@ function verifyMatchingSymbols(decl) {
}
const symbolInScope = findInScope(symbolOfNode.name);
if (!symbolInScope) {
if (symbolOfNode.declarations?.every(d => isLocalDeclaration(d) && d.getSourceFile() === decl.getSourceFile()) && !isSelfReference(node, symbolOfNode)) {
// The symbol is a local that needs to be copied into the scope.
scopeStack[scopeStack.length - 1].locals.set(symbolOfNode.name, { symbol: symbolOfNode, writeTarget: isInternal ? WriteTarget.Internal : WriteTarget.Both });
Copy link
Member

Choose a reason for hiding this comment

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

It seems moderately weird for this verification function to modify the locals, but not sure what's better.

Copy link
Member Author

Choose a reason for hiding this comment

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

I feel like I considered either renaming the function or returning the set of new locals to be appended before I paused this work.

Copy link
Member Author

Choose a reason for hiding this comment

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

@jakebailey any more thoughts on this? I agree this code isn’t great. Otherwise I think the current state is ready for review.

Copy link
Member

Choose a reason for hiding this comment

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

No, not really; I think it's fine for now. Thankfully I don't think the bundler has that high of a cognitive load nor changes that much 😄

@andrewbranch andrewbranch force-pushed the protocol-deduplication branch from fe72547 to e531c0b Compare March 1, 2024 23:03
Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

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

LGTM, though at some level I wonder if we should switch everything to export now such that future PRs which modify the top level API don't have to incur the diff change. But, another PR for that would be fine.

(I'm waffling on whether or not an export modifier is good or bad at this point; before we didn't have any so I was happy because it meant a smaller package.)

@andrewbranch andrewbranch merged commit 6d458e8 into microsoft:main Mar 4, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Possibly missing properties in the ts.server.protocol.CompilerOptions interface
4 participants