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

Add ReScript support #115

Merged
merged 9 commits into from
Oct 29, 2020
19 changes: 14 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# master

- Bind `readInlineData` for fragments annotated with `@inline`
- Generate a `commitLocalPayload` for any query annotated with `@raw_response_type`, to allow comitting local only payloads in a type safe way.
- _BREAKING CHANGE_ replace unsetValue with setValueToUndefined and setValueToNull
- clean bindings, renamed internal raw types and functions with names ending with `Raw`,
- use abstract records instead of Js.t objects for a more robust type-check and to avoid undefined fields
## Breaking changes

- _BREAKING CHANGE_ Replace unsetValue with setValueToUndefined and setValueToNull [#105](https://github.com/zth/reason-relay/pull/105) ([@tsnobip](https://github.com/tsnobip))

## New bindings

- Bind `readInlineData` for fragments annotated with `@inline` [#117](https://github.com/zth/reason-relay/pull/117) ([@zth](https://github.com/zth))
- Clean bindings, renamed internal raw types and functions with names ending with `Raw` [#105](https://github.com/zth/reason-relay/pull/105) ([@tsnobip](https://github.com/tsnobip))

## Fixes & misc

- Generate a `commitLocalPayload` for any query annotated with `@raw_response_type`, to allow comitting local only payloads in a type safe way. [#118](https://github.com/zth/reason-relay/pull/118) ([@zth](https://github.com/zth))
- Use abstract records instead of Js.t objects for a more robust type-check and to avoid undefined fields [#105](https://github.com/zth/reason-relay/pull/105) ([@tsnobip](https://github.com/tsnobip))
- Add support for parsing ReScript (.res) files [#115](https://github.com/zth/reason-relay/pull/115) ([@sorenhoyer](https://github.com/sorenhoyer))

# 0.11.0

Expand Down
5 changes: 5 additions & 0 deletions packages/reason-relay/language-plugin/src/@typings/graphql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "graphql";

declare module "graphql" {
const stripIgnoredCharacters: (source: string) => string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { DefinitionNode, DocumentNode } from "graphql";
import "relay-compiler";
import { Fragment, Parser, Root, Schema } from "relay-compiler";

declare module "relay-compiler" {
const convertASTDocuments: (
extendedSchema: Schema,
ast: DocumentNode[],
parser: (
schema: Schema,
documents: DefinitionNode[]
) => readonly (Root | Fragment)[]
) => readonly (Fragment | Root)[];
}
23 changes: 18 additions & 5 deletions packages/reason-relay/language-plugin/src/FindGraphQLTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { GraphQLTag } from "relay-compiler/lib/language/RelayLanguagePluginInter
const invariant = require("invariant");

function parseFile(text: string, file: string) {
if (!text.includes("[%relay.")) {
if (!text.includes("%relay")) {
return [];
}

invariant(
text.indexOf("[%relay.") >= 0,
text.indexOf("%relay") >= 0,
"RelayFileIRParser: Files should be filtered before passed to the " +
"parser, got unfiltered file `%s`.",
file
Expand All @@ -19,19 +19,32 @@ function parseFile(text: string, file: string) {
* regexp, but this will do just to get things working.
*/

const matched = text.match(
const matchedReason = text.match(
/(?<=\[%relay\.(query|fragment|mutation|subscription))([\s\S]*?)(?=];)/g
);

if (matched) {
if (matchedReason) {
// Removes {||} used in multiline Reason strings
return matched.map(text => ({
return matchedReason.map(text => ({
template: text.replace(/({\||\|})/g, ""),
keyName: null,
sourceLocationOffset: { line: 1, column: 1 }
}));
}

const matchedReScript = text.match(
/(?<=%relay\.(query|fragment|mutation|subscription)\()([\s\S]*?)(?=(\`\s*)\))(\`)/g
);

if (matchedReScript) {
// Removes `` used in multiline ReScript strings
return matchedReScript.map(text => ({
template: text.replace(/`/g, ""),
keyName: null,
sourceLocationOffset: { line: 1, column: 1 }
}));
}

return [];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { stripIgnoredCharacters } from "graphql";
import { join, resolve } from "path";
import { SourceLocation } from "relay-compiler/lib/core/IR";
import { find } from "../FindGraphQLTags";
import {
generateRelaySchema,
generateSchema,
parseGraphQLText,
} from "../test-utils";

const relaySchema = generateRelaySchema(
generateSchema(
resolve(join(__dirname, "..", "test-utils", "testSchema.graphql"))
)
);

const fragment = `
fragment SomeComponent_user on User {
id
}
`;

const query = `
query appQuery($userId: ID!) {
user(id: $userId) {
id
firstName
}
}
`;

describe("Language plugin tests", () => {
describe("RelayFindGraphQLTags", () => {
describe("ReScript Syntax", () => {
it("parses graphql templates", () => {
const graphqlTags = find(
`
module Fragment = %relay.fragment(
\`
${fragment}
\`
)

module Query = %relay.query(
\`
${query}
\`
)
`,
"test"
);

const parsedBodies = graphqlTags.map((tag) => {
const { definitions, schema } = parseGraphQLText(
relaySchema,
tag.template
);

return definitions && definitions.length && schema
? stripIgnoredCharacters(
((definitions[0] as any).loc as SourceLocation).source.body
)
: undefined;
});

expect(parsedBodies).toEqual([
stripIgnoredCharacters(fragment),
stripIgnoredCharacters(query),
]);
});
// describe("ReScript Syntax", () => {});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,87 +1,13 @@
import { Source, parse } from "graphql";
import * as fs from "fs";
import * as path from "path";
import * as RelayReasonGenerator from "../RelayReasonGenerator";
const getLanguagePlugin = require("../index");
import { printCode } from "../generator/Printer.gen";

const CompilerContext = require("relay-compiler/lib/core/CompilerContext");

import * as RelayIRTransforms from "relay-compiler/lib/core/RelayIRTransforms";

import { join, resolve } from "path";
import {
Parser,
// @ts-ignore
convertASTDocuments,
Root,
Schema,
Fragment,
} from "relay-compiler";

const create = require("relay-compiler").Schema.create;

function parseGraphQLText(
schema: any,
text: string
): {
definitions: ReadonlyArray<Fragment | Root>;
schema: any;
} {
const ast = parse(text);
const extendedSchema = schema.extend(ast);
const definitions = convertASTDocuments(
extendedSchema,
[ast],
Parser.transform.bind(Parser)
);
return {
definitions,
schema: extendedSchema,
};
}

const testSchema = create(
new Source(
fs.readFileSync(
path.resolve(path.join(__dirname, "testSchema.graphql")),
"utf8"
)
)
);
collapseString,
generate as generateCurryFunc,
generateSchema,
} from "../test-utils";

function collapseString(str: string) {
return str.replace(/\r?\n|\r|\t/g, "").replace(/\s+/g, " ");
}

function generate(text: string, options?: any, extraDefs: string = "") {
const relaySchema = testSchema.extend([
...RelayIRTransforms.schemaExtensions,
...getLanguagePlugin().schemaExtensions,
extraDefs,
]);
const { definitions, schema: extendedSchema } = parseGraphQLText(
relaySchema,
text
);

const ctx = new CompilerContext(extendedSchema)
.addAll(definitions)
.applyTransforms(RelayReasonGenerator.transforms);

return ctx
.documents()
.map(
(doc: any) =>
`// ${doc.name}.graphql\n${printCode(
RelayReasonGenerator.generate(extendedSchema, doc, {
normalizationIR: ctx.get(doc.name),
optionalInputFields: [],
...options,
})
)}`
)
.join("\n\n");
}
const generate = generateCurryFunc(
generateSchema(resolve(join(__dirname, "..", "test-utils", "testSchema.graphql")))
);

describe("Language plugin tests", () => {
describe("Query", () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/reason-relay/language-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ function getFileFilter(baseDir: string) {
);
return false;
}
return text.indexOf("[%relay.") >= 0;
return text.indexOf("[%relay.") >= 0 || text.indexOf("%relay.") >= 0;
};
}

module.exports = () => ({
inputExtensions: ["re"],
inputExtensions: ["re", "res"],
outputExtension: "re",
schemaExtensions: [],
typeGenerator: RelayReasonGenerator,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const collapseString = (str: string): string =>
str.replace(/\r?\n|\r|\t/g, "").replace(/\s+/g, " ");

export default collapseString;
36 changes: 36 additions & 0 deletions packages/reason-relay/language-plugin/src/test-utils/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CompilerContext, Schema } from "relay-compiler";
import { printCode } from "../generator/Printer.gen";
import * as RelayReasonGenerator from "../RelayReasonGenerator";
import generateRelaySchema from "./generateRelaySchema";
import parseGraphQLText from "./parseGraphQLText";

const generate = (testSchema: Schema) => (
text: string,
options?: any,
extraDefs: string = ""
): string => {
const { definitions, schema: extendedSchema } = parseGraphQLText(
generateRelaySchema(testSchema, extraDefs),
text
);

const ctx = new CompilerContext(extendedSchema)
.addAll(definitions)
.applyTransforms(RelayReasonGenerator.transforms);

return ctx
.documents()
.map(
(doc: any) =>
`// ${doc.name}.graphql\n${printCode(
RelayReasonGenerator.generate(extendedSchema, doc, {
normalizationIR: ctx.get(doc.name),
optionalInputFields: [],
...options,
})
)}`
)
.join("\n\n");
};

export default generate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { IRTransforms, Schema } from "relay-compiler";
const getLanguagePlugin = require("../index");

const generateRelaySchema = (testSchema: Schema, extraDefs: string = "") =>
testSchema.extend([
...IRTransforms.schemaExtensions,
...getLanguagePlugin().schemaExtensions,
extraDefs,
]);

export default generateRelaySchema;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { readFileSync } from "fs";
import { Source } from "graphql";
import { Schema } from "relay-compiler";

const create = require("relay-compiler").Schema.create;

const generateSchema = (path: string): Schema =>
create(new Source(readFileSync(path, "utf8")));

export default generateSchema;
13 changes: 13 additions & 0 deletions packages/reason-relay/language-plugin/src/test-utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import collapseString from "./collapseString";
import generate from "./generate";
import generateRelaySchema from "./generateRelaySchema";
import generateSchema from "./generateSchema";
import parseGraphQLText from "./parseGraphQLText";

export {
collapseString,
generate,
generateRelaySchema,
generateSchema,
parseGraphQLText,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { parse } from "graphql";
import {
convertASTDocuments,
Fragment,
Parser,
Root,
Schema,
} from "relay-compiler";

const parseGraphQLText = (
schema: Schema,
text: string
): {
definitions: ReadonlyArray<Fragment | Root>;
schema: Schema;
} => {
const ast = parse(text);

const extendedSchema = schema.extend(ast);

const definitions = convertASTDocuments(
extendedSchema,
[ast],
Parser.transform.bind(Parser)
);

return {
definitions,
schema: extendedSchema,
};
};

export default parseGraphQLText;