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

Fix components not refreshing during hmr #305

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 62 additions & 49 deletions crates/common_lang_types/src/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ use std::{error::Error, fmt};

use intern::Lookup;

use crate::{text_with_carats::text_with_carats, SourceFileName, Span, WithSpan};
use crate::{text_with_carats::text_with_carats, RelativePathToSourceFile, Span, WithSpan};

/// A source, which consists of a filename, and an optional span
/// indicating the subset of the file which corresponds to the
/// source.
/// A source, which consists of a path from the config's project root
/// to the source file, and an optional span indicating the subset of
/// the file which corresponds to the source.
///
/// TODO consider whether to replace the span with an index,
/// as this will probably mean that sources are more reusable
/// during watch mode.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TextSource {
pub path: SourceFileName,
pub struct RelativeTextSource {
pub path: RelativePathToSourceFile,
pub span: Option<Span>,
}

impl TextSource {
impl RelativeTextSource {
pub fn read_to_string(&self) -> (&str, String) {
// TODO maybe intern these or somehow avoid reading a bajillion times.
// This is especially important for when we display many errors.
Expand All @@ -33,40 +33,53 @@ impl TextSource {
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct EmbeddedLocation {
pub text_source: TextSource,
pub struct EmbeddedRelativeLocation {
pub text_source: RelativeTextSource,
/// The span is relative to the Source's span, not to the
/// entire source file.
pub span: Span,
}

impl std::fmt::Display for EmbeddedLocation {
/// What is happening here? EmbeddedRelativeLocation's only contain
/// a path that is relative to the Isograph config's project_root
/// field. Displaying the EmbeddedRelativeLocation involves reading the
/// file and displaying a subset of it.
///
/// The AbsoluteEmbeddedLocation struct knows how to turn that relative
/// path to an absolute path (i.e. it contains the absolute path to
/// the project_root) for use when reading the file.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct AbsoluteEmbeddedLocation {
pub embedded_location: EmbeddedRelativeLocation,
}

impl std::fmt::Display for AbsoluteEmbeddedLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (file_path, read_out_text) = self.text_source.read_to_string();
let text_with_carats = text_with_carats(&read_out_text, self.span);
let (file_path, read_out_text) = self.embedded_location.text_source.read_to_string();
let text_with_carats = text_with_carats(&read_out_text, self.embedded_location.span);

write!(f, "{}\n{}", file_path, text_with_carats)
}
}

impl From<EmbeddedLocation> for Location {
fn from(value: EmbeddedLocation) -> Self {
impl From<EmbeddedRelativeLocation> for Location {
fn from(value: EmbeddedRelativeLocation) -> Self {
Location::Embedded(value)
}
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Location {
Embedded(EmbeddedLocation),
Embedded(EmbeddedRelativeLocation),
Generated,
}

impl Location {
pub fn generated() -> Self {
Location::Generated
}
pub fn new(text_source: TextSource, span: Span) -> Self {
Location::Embedded(EmbeddedLocation::new(text_source, span))
pub fn new(text_source: RelativeTextSource, span: Span) -> Self {
Location::Embedded(EmbeddedRelativeLocation::new(text_source, span))
}

pub fn span(&self) -> Option<Span> {
Expand All @@ -76,16 +89,23 @@ impl Location {
}
}
}
impl EmbeddedLocation {
pub fn new(text_source: TextSource, span: Span) -> Self {
EmbeddedLocation { text_source, span }
impl EmbeddedRelativeLocation {
pub fn new(text_source: RelativeTextSource, span: Span) -> Self {
EmbeddedRelativeLocation { text_source, span }
}
}

impl fmt::Display for Location {
struct AbsoluteLocation {
pub location: Location,
}

impl fmt::Display for AbsoluteLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Location::Embedded(e) => e.fmt(f),
match self.location {
Location::Embedded(embedded_location) => {
let wrapper = AbsoluteEmbeddedLocation { embedded_location };
wrapper.fmt(f)
}
Location::Generated => {
write!(f, "<generated>")
}
Expand All @@ -108,7 +128,10 @@ impl<T: Error> Error for WithLocation<T> {

impl<T: fmt::Display> fmt::Display for WithLocation<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\n{}", self.item, self.location)
let absolute_location = AbsoluteLocation {
location: self.location,
};
write!(f, "{}\n{}", self.item, absolute_location)
}
}

Expand All @@ -131,7 +154,7 @@ impl<T> WithLocation<T> {
/// EmbeddedLocation or WithEmbeddedLocation
pub fn hack_to_with_span(self) -> WithSpan<T> {
let span = match self.location {
Location::Embedded(EmbeddedLocation { span, .. }) => span,
Location::Embedded(EmbeddedRelativeLocation { span, .. }) => span,
Location::Generated => Span::todo_generated(),
};
WithSpan {
Expand All @@ -142,47 +165,37 @@ impl<T> WithLocation<T> {
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct WithEmbeddedLocation<T> {
pub location: EmbeddedLocation,
pub struct WithEmbeddedRelativeLocation<T> {
pub location: EmbeddedRelativeLocation,
pub item: T,
}

impl<T: Error> Error for WithEmbeddedLocation<T> {
fn description(&self) -> &str {
#[allow(deprecated)]
self.item.description()
}
}

impl<T: fmt::Display> fmt::Display for WithEmbeddedLocation<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\n{}", self.item, self.location)
}
}

impl<T> WithEmbeddedLocation<T> {
pub fn new(item: T, location: EmbeddedLocation) -> Self {
WithEmbeddedLocation { item, location }
impl<T> WithEmbeddedRelativeLocation<T> {
pub fn new(item: T, location: EmbeddedRelativeLocation) -> Self {
WithEmbeddedRelativeLocation { item, location }
}

pub fn map<U>(self, map: impl FnOnce(T) -> U) -> WithEmbeddedLocation<U> {
WithEmbeddedLocation::new(map(self.item), self.location)
pub fn map<U>(self, map: impl FnOnce(T) -> U) -> WithEmbeddedRelativeLocation<U> {
WithEmbeddedRelativeLocation::new(map(self.item), self.location)
}

pub fn and_then<U, E>(
self,
map: impl FnOnce(T) -> Result<U, E>,
) -> Result<WithEmbeddedLocation<U>, E> {
Ok(WithEmbeddedLocation::new(map(self.item)?, self.location))
) -> Result<WithEmbeddedRelativeLocation<U>, E> {
Ok(WithEmbeddedRelativeLocation::new(
map(self.item)?,
self.location,
))
}

pub fn into_with_location(self) -> WithLocation<T> {
self.into()
}
}

impl<T> From<WithEmbeddedLocation<T>> for WithLocation<T> {
fn from(value: WithEmbeddedLocation<T>) -> Self {
impl<T> From<WithEmbeddedRelativeLocation<T>> for WithLocation<T> {
fn from(value: WithEmbeddedRelativeLocation<T>) -> Self {
WithLocation {
location: Location::Embedded(value.location),
item: value.item,
Expand Down
17 changes: 13 additions & 4 deletions crates/common_lang_types/src/span.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::{fmt, ops::Range};

use crate::{EmbeddedLocation, Location, TextSource, WithEmbeddedLocation, WithLocation};
use crate::{
EmbeddedRelativeLocation, Location, RelativeTextSource, WithEmbeddedRelativeLocation,
WithLocation,
};

// Invariant: end >= start
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
Expand Down Expand Up @@ -101,12 +104,18 @@ impl<T> WithSpan<T> {
}
}

pub fn to_with_location(self, text_source: TextSource) -> WithLocation<T> {
pub fn to_with_location(self, text_source: RelativeTextSource) -> WithLocation<T> {
WithLocation::new(self.item, Location::new(text_source, self.span))
}

pub fn to_with_embedded_location(self, text_source: TextSource) -> WithEmbeddedLocation<T> {
WithEmbeddedLocation::new(self.item, EmbeddedLocation::new(text_source, self.span))
pub fn to_with_embedded_location(
self,
text_source: RelativeTextSource,
) -> WithEmbeddedRelativeLocation<T> {
WithEmbeddedRelativeLocation::new(
self.item,
EmbeddedRelativeLocation::new(text_source, self.span),
)
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/common_lang_types/src/string_key_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ string_key_newtype!(IsographDirectiveName);

string_key_newtype!(FieldArgumentName);

string_key_newtype!(SourceFileName);
// This path is from the config's project root to the source file.
string_key_newtype!(RelativePathToSourceFile);

string_key_newtype!(ArtifactFileType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ pub(crate) fn generate_eager_reader_condition_artifact(
{}{reader_output_type}\n\
> = {{\n\
{}kind: \"EagerReaderArtifact\",\n\
{}resolver: ({{ data }}) => data.__typename === \"{concrete_type}\" ? data.link : null,\n\
{}resolver: ({{ firstParameter }}) => firstParameter.data.__typename === \"{concrete_type}\" ? firstParameter.data.link : null,\n\
{}readerAst,\n\
}};\n\n\
export default artifact;\n",
Expand Down
30 changes: 15 additions & 15 deletions crates/graphql_artifact_generation/src/iso_overload_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub(crate) fn build_iso_overload_artifact(
// of type TParam.
type IdentityWithParam<TParam extends object> = <TClientFieldReturn>(
clientField: (param: TParam) => TClientFieldReturn
) => (param: TParam) => TClientFieldReturn;
) => (data: { firstParameter: TParam }) => TClientFieldReturn;

// This is the type given it to client fields with @component.
// This means that the type of the exported iso literal is exactly
Expand All @@ -121,7 +121,7 @@ type IdentityWithParamComponent<TParam extends object> = <
TComponentProps = Record<PropertyKey, never>,
>(
clientComponentField: (data: TParam, componentProps: TComponentProps) => TClientFieldReturn
) => (data: TParam, componentProps: TComponentProps) => TClientFieldReturn;
) => (props: { firstParameter: TParam, additionalRuntimeProps: TComponentProps }) => TClientFieldReturn;

type WhitespaceCharacter = ' ' | '\\t' | '\\n';
type Whitespace<In> = In extends `${WhitespaceCharacter}${infer In}`
Expand Down Expand Up @@ -168,24 +168,24 @@ type MatchesWhitespaceAndString<
content.push_str(&entrypoint_overload);
}

content.push_str(
"
export function iso(_isographLiteralText: string):
content.push_str(match no_babel_transform {
false => {
"export function iso(clientFieldResolver: any):
| IdentityWithParam<any>
| IdentityWithParamComponent<any>
| IsographEntrypoint<any, any>
{\n",
);

content.push_str(match no_babel_transform {
false => {
" throw new Error('iso: Unexpected invocation at runtime. Either the Babel transform ' +
'was not set up, or it failed to identify this call site. Make sure it ' +
'is being used verbatim as `iso`. If you cannot use the babel transform, ' +
'set options.no_babel_transform to true in your Isograph config. ');"
{\n return (props: any) => {
return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps)
};"
}
true => {
" return (clientFieldResolver: any) => clientFieldResolver;"
"export function iso(_isographLiteralText: string):
| IdentityWithParam<any>
| IdentityWithParamComponent<any>
| IsographEntrypoint<any, any>
{\n return (clientFieldResolver: any) => (props: any) => {
return clientFieldResolver(props.firstParameter, props.additionalRuntimeProps)
};"
}
});

Expand Down
6 changes: 3 additions & 3 deletions crates/graphql_lang_types/src/directive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fmt;

use super::{write::write_arguments, NameValuePair};
use crate::GraphQLConstantValue;
use common_lang_types::{DirectiveArgumentName, DirectiveName, WithEmbeddedLocation};
use common_lang_types::{DirectiveArgumentName, DirectiveName, WithEmbeddedRelativeLocation};
use intern::Lookup;
use serde::{
de::{self, value::SeqDeserializer, IntoDeserializer, MapAccess},
Expand All @@ -13,13 +13,13 @@ use thiserror::Error;
// TODO maybe this should be NameAndArguments and a field should be the same thing...?
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct GraphQLDirective<T> {
pub name: WithEmbeddedLocation<DirectiveName>,
pub name: WithEmbeddedRelativeLocation<DirectiveName>,
pub arguments: Vec<NameValuePair<DirectiveArgumentName, T>>,
}

impl<T: fmt::Display> fmt::Display for GraphQLDirective<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "@{}", self.name)?;
write!(f, "@{}", self.name.item)?;
write_arguments(f, &self.arguments)?;
Ok(())
}
Expand Down
Loading
Loading