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

Support custom module-name generator in relay-compiler #2958

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
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);
});
});

martinandert marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -175,6 +178,7 @@ module.exports = {
transformASTSchema: ASTConvert.transformASTSchema,

getReaderSourceDefinitionName,
getGenerateModuleNameFunction,
martinandert marked this conversation as resolved.
Show resolved Hide resolved

writeRelayGeneratedFile,

Expand Down