Skip to content
This repository was archived by the owner on Sep 27, 2023. It is now read-only.

Commit 72bb0e0

Browse files
committed
[Transform] Imported test fixtures from babel-plugin-relay
1 parent 9af2db6 commit 72bb0e0

File tree

138 files changed

+17068
-143
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+17068
-143
lines changed

transform/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@types/jest": "^22.0.1",
2929
"@types/node": "^9.3.0",
3030
"jest": "^22.1.4",
31+
"relay-test-utils": "^1.5.0-rc.1",
3132
"ts-jest": "^22.0.1"
3233
},
3334
"jest": {

transform/src/Options.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface Options {
1111
isDevVariable?: string;
1212
buildCommand?: string;
1313
isDevelopment?: boolean;
14+
substituteVariables?: boolean;
1415
}
1516

1617
export interface NormalizedOptions {
@@ -20,6 +21,7 @@ export interface NormalizedOptions {
2021
isDevVariable?: string;
2122
buildCommand?: string;
2223
isDevelopment?: boolean;
24+
substituteVariables?: boolean;
2325
}
2426

2527
const dotJsonLength = '.json'.length;
@@ -49,7 +51,7 @@ export function normalizeOptions(options: Options): NormalizedOptions {
4951
relayQLTransformer: options.schema ? new RelayQLTransformer(readGraphQLSchema(options.schema), {
5052
inputArgumentName: 'input',
5153
snakeCase: false,
52-
substituteVariables: true,
54+
substituteVariables: options.substituteVariables || false,
5355
validator: null,
5456
}) : undefined,
5557
};

transform/src/RelayQLTransformer.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,20 @@ export class RelayQLTransformer {
8080
node: ts.TaggedTemplateExpression,
8181
options: TextTransformOptions,
8282
): Printable {
83+
const documentName = sanitizeDocumentName(options.documentName);
84+
const opts = {
85+
documentName,
86+
propName: options.propName,
87+
enableValidation: options.enableValidation,
88+
tagName: options.tagName,
89+
};
8390
const {
8491
substitutions,
8592
templateText,
8693
variableNames,
87-
} = this.processTemplateLiteral(node, options.documentName);
88-
const documentText = this.processTemplateText(templateText, options);
89-
const definition = this.processDocumentText(documentText, options);
94+
} = this.processTemplateLiteral(node, documentName);
95+
const documentText = this.processTemplateText(templateText, opts);
96+
const definition = this.processDocumentText(documentText, opts);
9097

9198
const Printer = RelayQLPrinter(this.options);
9299
return new Printer(options.tagName, variableNames).print(
@@ -119,14 +126,14 @@ export class RelayQLTransformer {
119126
};
120127
}
121128
chunks.push(template.head.text);
129+
let previousChunk = template.head.text;
122130
template.templateSpans.forEach((element, ii) => {
123131
const literal = element.literal;
124132
const chunk = literal.text;
125-
chunks.push(chunk);
126133
const name = 'RQL_' + ii;
127134
const value = element.expression;
128135
substitutions.push({ name, value });
129-
if (/:\s*$/.test(chunk)) {
136+
if (/:\s*$/.test(previousChunk)) {
130137
if (!this.options.substituteVariables) {
131138
throw new Error(util.format(
132139
"You supplied a GraphQL document named `%s` that uses template " +
@@ -140,6 +147,8 @@ export class RelayQLTransformer {
140147
} else {
141148
chunks.push('...' + name);
142149
}
150+
chunks.push(chunk);
151+
previousChunk = chunk;
143152
});
144153
return { substitutions, templateText: chunks.join('').trim(), variableNames };
145154
}
@@ -151,9 +160,8 @@ export class RelayQLTransformer {
151160
templateText: string,
152161
{ documentName, propName }: TextTransformOptions,
153162
): string {
154-
const pattern = /^(fragment|mutation|query|subscription)\s*(\w*)?([\s\S]*)/;
163+
const pattern = /^[\s\n]*(fragment|mutation|query|subscription)\s*(\w*)?([\s\S]*)/;
155164
const matches = pattern.exec(templateText);
156-
const sanitizedDocumentName = sanitizeDocumentName(documentName);
157165
if (!matches) {
158166
throw new Error(util.format(
159167
"You supplied a GraphQL document named `%s` with invalid syntax. It " +
@@ -167,7 +175,7 @@ export class RelayQLTransformer {
167175
// Allow `fragment on Type {...}`.
168176
if (type === 'fragment' && name === 'on') {
169177
name =
170-
sanitizedDocumentName + (propName ? '_' + capitalize(propName) : '') + 'RelayQL';
178+
documentName + (propName ? '_' + capitalize(propName) : '') + 'RelayQL';
171179
rest = 'on' + rest;
172180
}
173181
const definitionName = capitalize(name);

transform/src/RelayTransformError.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { Location } from "graphql";
22

3-
export class RelayTransformError {
3+
export class RelayTransformError extends Error {
44
message: string;
55
loc: Location | null;
66
stack: string;
77

88
constructor(message: string, loc: Location | undefined) {
9-
this.message = message;
9+
super(message);
1010
this.loc = loc || null;
11-
this.stack = new Error().stack || "";
11+
Object.setPrototypeOf(this, RelayTransformError.prototype);
1212
}
1313
}

transform/src/compileGraphQLTag.ts

-6
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,12 @@ function createAST(
8282
const buildCommand =
8383
(opts.buildCommand) || 'relay-compiler';
8484

85-
// Fallback is 'true'
86-
const isDevelopment =
87-
(process.env.NODE_ENV) !== 'production';
88-
8985
const modernNode = createModernNode(ctx, opts, graphqlDefinition, fileName);
9086
if (isCompatMode) {
91-
console.log('Launching compat mode!');
9287
const result = createCompatNode(
9388
modernNode,
9489
createClassicNode(ctx, bindingsAtNode(node), node, graphqlDefinition, opts),
9590
);
96-
console.log('Done generating stuff');
9791
if (setSourceMapRange) {
9892
ts.setSourceMapRange(result, ts.getSourceMapRange(node));
9993
}

transform/src/compileRelayQLTag.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ export function compileRelayQLTag(
1717
tagName: string,
1818
enableValidation: boolean,
1919
): ts.Expression {
20-
const result = transformer.transform(node, {
21-
documentName,
22-
propName,
23-
tagName,
24-
enableValidation,
25-
});
26-
return result;
20+
try {
21+
return transformer.transform(node, {
22+
documentName,
23+
propName,
24+
tagName,
25+
enableValidation,
26+
});
27+
} catch (e) {
28+
throw createTransformError(e);
29+
}
2730
}

transform/src/index.ts

+38-2
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,45 @@ interface ExtraNode {
1616
transformFlags: TransformFlags;
1717
}
1818

19+
function insertVarDecl(varDecl: ts.Statement, insertInto: ts.NodeArray<ts.Statement>): ts.Statement[] {
20+
const useStrictIdx = insertInto.findIndex(
21+
stmt => ts.isExpressionStatement(stmt) && ts.isLiteralExpression(stmt.expression) && stmt.expression.text == 'use strict'
22+
);
23+
if (useStrictIdx >= 0) {
24+
const newStmts = insertInto.slice(0);
25+
newStmts.splice(useStrictIdx + 1, 0, varDecl);
26+
return newStmts;
27+
}
28+
return [varDecl, ...insertInto];
29+
}
30+
1931
function visitor(ctx: ts.TransformationContext, sf: ts.SourceFile, opts: NormalizedOptions): ts.Visitor {
2032
const fileName = sf.fileName;
33+
let i = 0;
34+
const varDecls: ts.VariableDeclaration[] = [];
35+
function declareVar(id: ts.Identifier): void {
36+
varDecls.push(ts.createVariableDeclaration(id, undefined, undefined));
37+
}
38+
let scopeLevel = 0;
2139
const visit = (node: ts.Node): ts.Node => {
2240
// Easy bailout if there are not ES2015 features used
2341
if (((node as any as ExtraNode).transformFlags & TransformFlags.ContainsES2015) !== TransformFlags.ContainsES2015) {
2442
return node;
2543
}
44+
if (ts.isBlock(node)) {
45+
scopeLevel++;
46+
}
2647

2748
if (ts.isTaggedTemplateExpression(node)) {
2849
const ast = getValidGraphQLTag(node);
2950
if (ast) {
30-
return compileGraphQLTag(ctx, opts, node, ast, fileName);
51+
const res = compileGraphQLTag(ctx, opts, node, ast, fileName);
52+
if (scopeLevel > 0) {
53+
const id = ts.createIdentifier('__graphql$' + i++);
54+
declareVar(id);
55+
return ts.createLogicalOr(id, ts.createAssignment(id, res));
56+
}
57+
return res;
3158
}
3259

3360
const relayQLTag = getValidRelayQLTag(node);
@@ -43,7 +70,16 @@ function visitor(ctx: ts.TransformationContext, sf: ts.SourceFile, opts: Normali
4370
return result;
4471
}
4572
}
46-
return ts.visitEachChild(node, visit, ctx);
73+
const res = ts.visitEachChild(node, visit, ctx);
74+
75+
if (ts.isSourceFile(res) && varDecls.length > 0) {
76+
const varDecl = ts.createVariableStatement(undefined, ts.createVariableDeclarationList(varDecls));
77+
return ts.updateSourceFileNode(res, insertVarDecl(varDecl, res.statements));
78+
}
79+
if (ts.isBlock(node)) {
80+
scopeLevel--;
81+
}
82+
return res;
4783
};
4884

4985
return visit;

transform/test/TSTransform-test.ts

+77-23
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,90 @@ import { transformer } from '../src';
22
import * as ts from 'typescript';
33
import * as path from 'path';
44
import { Options } from '../src/Options';
5+
import { generateTestsFromFixtures } from 'relay-test-utils/lib/RelayModernTestUtils'
56

6-
function transformWithOptions(options: Options, contents: string, fileName: string): ts.TranspileOutput {
7-
return ts.transpileModule(contents, {
8-
compilerOptions: {
9-
target: ts.ScriptTarget.ES5,
10-
sourceMap: true,
11-
},
12-
fileName: fileName,
13-
transformers: {
14-
before: [transformer(options)],
15-
},
16-
});
7+
function transformWithOptions(options: Options, fileName: string) {
8+
return (text, providedFileName?: string) =>
9+
ts.transpileModule(text, {
10+
compilerOptions: {
11+
target: ts.ScriptTarget.ES2017,
12+
jsx: ts.JsxEmit.Preserve,
13+
sourceMap: false,
14+
},
15+
fileName: fileName,
16+
transformers: {
17+
before: [transformer(options)],
18+
},
19+
}).outputText;
1720
}
1821

19-
const schemaPath = path.resolve(__dirname, 'testschema.rfc.graphql');
22+
const schemaPath = path.resolve(__dirname, 'testschema.graphql');
23+
const oldSchemaPath = path.resolve(__dirname, 'testschema.old.graphql');
2024

2125
describe('TSTransform', () => {
22-
it('Modern should compile', async () => {
23-
const text = 'createFragmentContainer(MyComponent, {todo: graphql`fragment MyFragment_todo on Node { id }`})';
26+
generateTestsFromFixtures(`${__dirname}/fixtures-modern`, transformWithOptions({}, '/test/MyComponent.tsx'));
2427

25-
expect(transformWithOptions({ isDevVariable: 'IS_DEV', artifactDirectory: '/testing/artifacts' }, text, '/test/MyComponent.ts')).toMatchSnapshot('modern test');
26-
});
27-
it('Compat should compile', async () => {
28-
const text = 'createFragmentContainer(MyComponent, {todo: graphql`fragment MyFragment_todo on Node { id }`})';
28+
generateTestsFromFixtures(
29+
`${__dirname}/fixtures-compat`,
30+
transformWithOptions({
31+
compat: true,
32+
schema: schemaPath,
33+
substituteVariables: true,
34+
}, '/test/MyComponent.tsx'),
35+
);
2936

30-
expect(transformWithOptions({ isDevVariable: 'IS_DEV', artifactDirectory: '/testing/artifacts', schema: schemaPath, compat: true }, text, '/test/MyComponent.ts')).toMatchSnapshot('compat test');
31-
});
32-
it('Classic should compile', async () => {
33-
const text = 'createFragmentContainer(MyComponent, {todo: () => Relay.QL`fragment on Node { id }`})';
37+
generateTestsFromFixtures(
38+
`${__dirname}/fixtures-compat`,
39+
transformWithOptions({
40+
compat: true,
41+
schema: schemaPath,
42+
substituteVariables: true,
43+
}, '/test/MyComponent.tsx'),
44+
);
45+
46+
generateTestsFromFixtures(
47+
`${__dirname}/fixtures-classic`,
48+
transformWithOptions({
49+
schema: oldSchemaPath,
50+
substituteVariables: true,
51+
}, '/test/MyComponent.tsx'),
52+
);
53+
54+
describe('`development` option', () => {
55+
it('tests the hash when `development` is set', () => {
56+
expect(
57+
transformWithOptions({ isDevelopment: true }, '/test/TestFrag.ts')(
58+
'graphql`fragment TestFrag on Node { id }`',
59+
),
60+
).toMatchSnapshot();
61+
});
62+
63+
it('tests the hash when `isDevVariable` is set', () => {
64+
expect(
65+
transformWithOptions({ isDevVariable: 'IS_DEV' }, '/test/TestFrag.ts')(
66+
'graphql`fragment TestFrag on Node { id }`',
67+
),
68+
).toMatchSnapshot();
69+
});
70+
71+
it('uses a custom build command in message', () => {
72+
expect(
73+
transformWithOptions(
74+
{
75+
buildCommand: 'relay-build',
76+
isDevelopment: true,
77+
},
78+
'/test/TestFrag.ts',
79+
)('graphql`fragment TestFrag on Node { id }`'),
80+
).toMatchSnapshot();
81+
});
3482

35-
expect(transformWithOptions({ isDevVariable: 'IS_DEV', artifactDirectory: '/testing/artifacts', schema: schemaPath }, text, '/test/MyComponent.ts')).toMatchSnapshot('classic test');
83+
it('does not test the hash when `development` is not set', () => {
84+
expect(
85+
transformWithOptions({}, '/test/TestFrag.ts')(
86+
'graphql`fragment TestFrag on Node { id }`',
87+
),
88+
).toMatchSnapshot();
89+
});
3690
});
3791
});

0 commit comments

Comments
 (0)