Skip to content

Commit

Permalink
Relay Compiler: Support custom getModuleName function
Browse files Browse the repository at this point in the history
  • Loading branch information
martinandert committed May 22, 2020
1 parent 9593d57 commit d95d5d7
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 17 deletions.
7 changes: 7 additions & 0 deletions packages/relay-compiler/bin/RelayCompilerBin.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ const options = {
type: 'boolean',
default: false,
},
generateModuleNameFunction: {
describe:
'A function (or path to a module exporting this function) which will generate a module name for a given file path.',
demandOption: false,
type: 'string',
array: false,
},
persistFunction: {
describe:
'An async function (or path to a module exporting this function) which will persist the query text and return the id.',
Expand Down
37 changes: 37 additions & 0 deletions packages/relay-compiler/bin/RelayCompilerMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import type {
PluginInitializer,
PluginInterface,
} from '../language/RelayLanguagePluginInterface';
import getModuleName from '../util/getModuleName';

export type Config = {|
schema: string,
Expand All @@ -60,6 +61,7 @@ export type Config = {|
noFutureProofEnums: boolean,
eagerESModules?: boolean,
language: string | PluginInitializer,
generateModuleNameFunction?: ?string | ?((filePath: string) => string),
persistFunction?: ?string | ?((text: string) => Promise<string>),
repersist: boolean,
artifactDirectory?: ?string,
Expand Down Expand Up @@ -166,6 +168,38 @@ function getLanguagePlugin(
}
}

function getGenerateModuleNameFunction(
config: Config,
): (filePath: string) => string {
const configValue = config.generateModuleNameFunction;
if (configValue == null) {
return getModuleName;
} else if (typeof configValue === 'string') {
try {
// eslint-disable-next-line no-eval
const generateModuleNameFunction = eval('require')(
path.resolve(process.cwd(), configValue),
);
if (generateModuleNameFunction.default) {
return generateModuleNameFunction.default;
}
return generateModuleNameFunction;
} catch (err) {
const e = new Error(
`Unable to load generateModuleNameFunction ${configValue}: ${err.message}`,
);
e.stack = err.stack;
throw e;
}
} else if (typeof configValue === 'function') {
return configValue;
} else {
throw new Error(
'Expected generateModuleNameFunction to be a path string or a function.',
);
}
}

function getPersistQueryFunction(
config: Config,
): ?(text: string) => Promise<string> {
Expand Down Expand Up @@ -285,13 +319,15 @@ function getCodegenRunner(config: Config): CodegenRunner {
const languagePlugin = getLanguagePlugin(config.language, {
eagerESModules: config.eagerESModules === true,
});
const generateModuleNameFunction = getGenerateModuleNameFunction(config);
const persistQueryFunction = getPersistQueryFunction(config);
const inputExtensions = config.extensions || languagePlugin.inputExtensions;
const outputExtension = languagePlugin.outputExtension;
const sourceParserName = inputExtensions.join('/');
const sourceWriterName = outputExtension;
const sourceModuleParser = RelaySourceModuleParser(
languagePlugin.findGraphQLTags,
generateModuleNameFunction,
languagePlugin.getFileFilter,
);
const providedArtifactDirectory = config.artifactDirectory;
Expand Down Expand Up @@ -508,6 +544,7 @@ function hasWatchmanRootFile(testPath: string): boolean {
module.exports = {
getCodegenRunner,
getLanguagePlugin,
getGenerateModuleNameFunction,
getWatchConfig,
hasWatchmanRootFile,
main,
Expand Down
16 changes: 16 additions & 0 deletions packages/relay-compiler/bin/__fixtures__/name-generator-module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @emails oncall+relay
*/

// flowlint ambiguous-object-type:error

'use strict';

module.exports = (filePath: string): string => filePath.toUpperCase();
33 changes: 33 additions & 0 deletions packages/relay-compiler/bin/__tests__/RelayCompilerMain-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
const RelayCompilerMain = require('../RelayCompilerMain');
const RelayFileWriter = require('../../codegen/RelayFileWriter');
const RelayLanguagePluginJavaScript = require('../../language/javascript/RelayLanguagePluginJavaScript');
const getModuleName = require('../../util/getModuleName');

const path = require('path');

Expand All @@ -23,6 +24,7 @@ const {testSchemaPath} = require('relay-test-utils-internal');
const {
getCodegenRunner,
getLanguagePlugin,
getGenerateModuleNameFunction,
getWatchConfig,
main,
} = RelayCompilerMain;
Expand Down Expand Up @@ -334,6 +336,37 @@ describe('RelayCompilerMain', () => {
});
});

describe('getGenerateModuleNameFunction', () => {
it('uses the builtin module name generator if not configured', () => {
expect(config.generateModuleNameFunction).toBeUndefined();

expect(getGenerateModuleNameFunction(config)).toBe(getModuleName);
});

it('loads a module name generator from a local module', () => {
config = {
...config,
generateModuleNameFunction: path.join(
__dirname,
'..',
'__fixtures__',
'name-generator-module.js',
),
};
const generateModuleName = getGenerateModuleNameFunction(config);
expect(generateModuleName('foo')).toEqual('FOO');
});

it('accepts a module name generator function', () => {
const generate = jest.fn();
config = {
...config,
generateModuleNameFunction: generate,
};
expect(getGenerateModuleNameFunction(config)).toBe(generate);
});
});

describe('concerning the codegen runner', () => {
const options = {
schema: testSchemaPath,
Expand Down
2 changes: 2 additions & 0 deletions packages/relay-compiler/core/JSModuleParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

const FindGraphQLTags = require('../language/javascript/FindGraphQLTags');
const RelaySourceModuleParser = require('./RelaySourceModuleParser');
const getModuleName = require('../util/getModuleName');

import type {SourceModuleParser} from './RelaySourceModuleParser';

const JSModuleParser: SourceModuleParser = RelaySourceModuleParser(
FindGraphQLTags.find,
getModuleName,
);

module.exports = JSModuleParser;
13 changes: 10 additions & 3 deletions packages/relay-compiler/core/RelayFindGraphQLTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

const RelayCompilerCache = require('../util/RelayCompilerCache');

const getModuleName = require('../util/getModuleName');
const graphql = require('graphql');
const path = require('path');
const util = require('util');
Expand All @@ -29,6 +28,7 @@ const cache = new RelayCompilerCache('RelayFindGraphQLTags', 'v1');

function memoizedFind(
tagFinder: GraphQLTagFinder,
generateModuleName: (filePath: string) => string,
text: string,
baseDir: string,
file: File,
Expand All @@ -40,17 +40,24 @@ function memoizedFind(
);
return cache.getOrCompute(
file.hash,
find.bind(null, tagFinder, text, path.join(baseDir, file.relPath)),
find.bind(
null,
tagFinder,
generateModuleName,
text,
path.join(baseDir, file.relPath),
),
);
}

function find(
tagFinder: GraphQLTagFinder,
generateModuleName: (filePath: string) => string,
text: string,
absPath: string,
): $ReadOnlyArray<string> {
const tags = tagFinder(text, absPath);
const moduleName = getModuleName(absPath);
const moduleName = generateModuleName(absPath);
tags.forEach(tag => validateTemplate(tag, moduleName, absPath));
return tags.map(tag => tag.template);
}
Expand Down
27 changes: 15 additions & 12 deletions packages/relay-compiler/core/RelaySourceModuleParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const parseGraphQL = Profiler.instrument(GraphQL.parse, 'GraphQL.parse');

module.exports = (
tagFinder: GraphQLTagFinder,
generateModuleName: (filePath: string) => string,
getFileFilter?: GetFileFilter,
): SourceModuleParser => {
const memoizedTagFinder = memoizedFind.bind(null, tagFinder);
Expand Down Expand Up @@ -80,18 +81,20 @@ module.exports = (

const astDefinitions = [];
const sources = [];
memoizedTagFinder(text, baseDir, file).forEach(template => {
const source = new GraphQL.Source(template, file.relPath);
const ast = parseGraphQL(source);
invariant(
ast.definitions.length,
'RelaySourceModuleParser: Expected GraphQL text to contain at least one ' +
'definition (fragment, mutation, query, subscription), got `%s`.',
template,
);
sources.push(source.body);
astDefinitions.push(...ast.definitions);
});
memoizedTagFinder(generateModuleName, text, baseDir, file).forEach(
template => {
const source = new GraphQL.Source(template, file.relPath);
const ast = parseGraphQL(source);
invariant(
ast.definitions.length,
'RelaySourceModuleParser: Expected GraphQL text to contain at least one ' +
'definition (fragment, mutation, query, subscription), got `%s`.',
template,
);
sources.push(source.body);
astDefinitions.push(...ast.definitions);
},
);

return {
document: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@
'use strict';

const FindGraphQLTags = require('../../language/javascript/FindGraphQLTags');
const getModuleName = require('../../util/getModuleName');
const RelayFindGraphQLTags = require('../RelayFindGraphQLTags');

describe('RelayFindGraphQLTags', () => {
function find(text, absPath: string = '/path/to/FindGraphQLTags.js') {
return RelayFindGraphQLTags.find(FindGraphQLTags.find, text, absPath);
return RelayFindGraphQLTags.find(
FindGraphQLTags.find,
getModuleName,
text,
absPath,
);
}

describe('query parsing', () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/relay-compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ const getSchemaInstance = require('./runner/getSchemaInstance');
const md5 = require('./util/md5');
const writeRelayGeneratedFile = require('./codegen/writeRelayGeneratedFile');

const {main} = require('./bin/RelayCompilerMain');
const {
main,
getGenerateModuleNameFunction,
} = require('./bin/RelayCompilerMain');
const {SourceControlMercurial} = require('./codegen/SourceControl');
const {
getReaderSourceDefinitionName,
Expand Down Expand Up @@ -177,6 +180,7 @@ module.exports = {

getReaderSourceDefinitionName,
getSourceDefinitionName,
getGenerateModuleNameFunction,

writeRelayGeneratedFile,

Expand Down

0 comments on commit d95d5d7

Please sign in to comment.