Skip to content

Commit

Permalink
Expose custom scalars configuration from CLI. (#2745)
Browse files Browse the repository at this point in the history
Summary:
Closes #2162.

In preparation of #2518, as requested by josephsavona we should have CLI versions of each possible argument, hereby CLI support for custom scalars.

I ended up going with the dot-notation syntax of yargs, which seems a little foreign (to me), but it comes builtin with yargs. Let me know what you think.

E.g.

```
$ relay-compiler --customScalars.URL=String --customScalars.Date=String
```
Pull Request resolved: #2745

Reviewed By: kassens

Differential Revision: D15724870

Pulled By: jstejada

fbshipit-source-id: 62d7a8766064c9355dad07ae7a94251fa4d047b6
  • Loading branch information
alloy authored and facebook-github-bot committed Jun 11, 2019
1 parent 360bc2c commit 553e331
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 89 deletions.
11 changes: 11 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ const INCLUDE_GLOBS = [
'!**/__tests__/**',
'!**/__flowtests__/**',
'!**/__mocks__/**',
'!**/node_modules/**',
];

const builds = [
Expand Down Expand Up @@ -262,6 +263,16 @@ const modules = gulp.parallel(
return gulp
.src(
INCLUDE_GLOBS,










{
cwd: path.join(PACKAGES, build.package),
}
Expand Down
9 changes: 9 additions & 0 deletions packages/relay-compiler/bin/RelayCompilerBin.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ const argv = yargs
type: 'string',
default: null,
},
customScalars: {
describe:
'Mappings from custom scalars in your schema to built-in GraphQL ' +
'types, for type emission purposes. (Uses yargs dot-notation, e.g. ' +
'--customScalars.URL=String)',
// $FlowFixMe
type: 'object',
default: {},
},
})
.help().argv;

Expand Down
151 changes: 81 additions & 70 deletions packages/relay-compiler/bin/RelayCompilerMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,32 @@ const {
schemaExtensions,
} = RelayIRTransforms;

import type {ScalarTypeMapping} from '../language/javascript/RelayFlowTypeTransformers';
import type {WriteFilesOptions} from '../codegen/CodegenRunner';
import type {GraphQLSchema} from 'graphql';
import type {
PluginInitializer,
PluginInterface,
} from '../language/RelayLanguagePluginInterface';

type Options = {|
schema: string,
src: string,
extensions: Array<string>,
include: Array<string>,
exclude: Array<string>,
verbose: boolean,
watchman: boolean,
watch?: ?boolean,
validate: boolean,
quiet: boolean,
persistOutput: ?string,
noFutureProofEnums: boolean,
language: string,
artifactDirectory: ?string,
customScalars?: ScalarTypeMapping,
|};

function buildWatchExpression(options: {
extensions: Array<string>,
include: Array<string>,
Expand Down Expand Up @@ -128,45 +147,28 @@ function getLanguagePlugin(language: string): PluginInterface {
}
}

async function main(options: {
schema: string,
src: string,
extensions: Array<string>,
include: Array<string>,
exclude: Array<string>,
verbose: boolean,
watchman: boolean,
watch?: ?boolean,
validate: boolean,
quiet: boolean,
persistOutput: ?string,
noFutureProofEnums: boolean,
language: string,
artifactDirectory: ?string,
}) {
const schemaPath = path.resolve(process.cwd(), options.schema);
if (!fs.existsSync(schemaPath)) {
throw new Error(`--schema path does not exist: ${schemaPath}`);
async function main(options: Options) {
const schema = path.resolve(process.cwd(), options.schema);
if (!fs.existsSync(schema)) {
throw new Error(`--schema path does not exist: ${schema}`);
}
const srcDir = path.resolve(process.cwd(), options.src);
if (!fs.existsSync(srcDir)) {
throw new Error(`--src path does not exist: ${srcDir}`);
const src = path.resolve(process.cwd(), options.src);
if (!fs.existsSync(src)) {
throw new Error(`--src path does not exist: ${src}`);
}

let persistedQueryPath = options.persistOutput;
if (typeof persistedQueryPath === 'string') {
persistedQueryPath = path.resolve(process.cwd(), persistedQueryPath);
const persistOutputDir = path.dirname(persistedQueryPath);
let persistOutput = options.persistOutput;
if (typeof persistOutput === 'string') {
persistOutput = path.resolve(process.cwd(), persistOutput);
const persistOutputDir = path.dirname(persistOutput);
if (!fs.existsSync(persistOutputDir)) {
throw new Error(
`--persist-output path does not exist: ${persistedQueryPath}`,
);
throw new Error(`--persist-output path does not exist: ${persistOutput}`);
}
}
if (options.watch && !options.watchman) {
throw new Error('Watchman is required to watch for changes.');
}
if (options.watch && !hasWatchmanRootFile(srcDir)) {
if (options.watch && !hasWatchmanRootFile(src)) {
throw new Error(
`
--watch requires that the src directory have a valid watchman "root" file.
Expand All @@ -176,86 +178,107 @@ Root files can include:
- A .hg/ Mercurial folder
- A .watchmanconfig file
Ensure that one such file exists in ${srcDir} or its parents.
Ensure that one such file exists in ${src} or its parents.
`.trim(),
);
}
if (options.verbose && options.quiet) {
throw new Error("I can't be quiet and verbose at the same time");
}

const watchman = options.watchman && (await WatchmanClient.isAvailable());

const codegenRunner = getCodegenRunner({
...options,
persistOutput,
schema,
src,
watchman,
});

if (!options.validate && !options.watch && watchman) {
// eslint-disable-next-line no-console
console.log('HINT: pass --watch to keep watching for changes.');
}

const result = options.watch
? await codegenRunner.watchAll()
: await codegenRunner.compileAll();

if (result === 'ERROR') {
process.exit(100);
}
if (options.validate && result !== 'NO_CHANGES') {
process.exit(101);
}
}

function getCodegenRunner(options: Options): CodegenRunner {
const reporter = new ConsoleReporter({
verbose: options.verbose,
quiet: options.quiet,
});
const useWatchman = options.watchman && (await WatchmanClient.isAvailable());

const schema = getSchema(schemaPath);

const schema = getSchema(options.schema);
const languagePlugin = getLanguagePlugin(options.language);

const inputExtensions = options.extensions || languagePlugin.inputExtensions;
const outputExtension = languagePlugin.outputExtension;

const sourceParserName = inputExtensions.join('/');
const sourceWriterName = outputExtension;

const sourceModuleParser = RelaySourceModuleParser(
languagePlugin.findGraphQLTags,
);

const providedArtifactDirectory = options.artifactDirectory;
const artifactDirectory =
providedArtifactDirectory != null
? path.resolve(process.cwd(), providedArtifactDirectory)
: null;

const generatedDirectoryName = artifactDirectory || '__generated__';

const sourceSearchOptions = {
extensions: inputExtensions,
include: options.include,
exclude: ['**/*.graphql.*', ...options.exclude], // Do not include artifacts
exclude: ['**/*.graphql.*', ...options.exclude],
};
const graphqlSearchOptions = {
extensions: ['graphql'],
include: options.include,
exclude: [path.relative(srcDir, schemaPath)].concat(options.exclude),
exclude: [path.relative(options.src, options.schema)].concat(
options.exclude,
),
};

const parserConfigs = {
[sourceParserName]: {
baseDir: srcDir,
baseDir: options.src,
getFileFilter: sourceModuleParser.getFileFilter,
getParser: sourceModuleParser.getParser,
getSchema: () => schema,
watchmanExpression: useWatchman
watchmanExpression: options.watchman
? buildWatchExpression(sourceSearchOptions)
: null,
filepaths: useWatchman
filepaths: options.watchman
? null
: getFilepathsFromGlob(srcDir, sourceSearchOptions),
: getFilepathsFromGlob(options.src, sourceSearchOptions),
},
graphql: {
baseDir: srcDir,
baseDir: options.src,
getParser: DotGraphQLParser.getParser,
getSchema: () => schema,
watchmanExpression: useWatchman
watchmanExpression: options.watchman
? buildWatchExpression(graphqlSearchOptions)
: null,
filepaths: useWatchman
filepaths: options.watchman
? null
: getFilepathsFromGlob(srcDir, graphqlSearchOptions),
: getFilepathsFromGlob(options.src, graphqlSearchOptions),
},
};
const writerConfigs = {
[sourceWriterName]: {
writeFiles: getRelayFileWriter(
srcDir,
options.src,
languagePlugin,
options.noFutureProofEnums,
artifactDirectory,
persistedQueryPath,
options.persistOutput,
options.customScalars,
),
isGeneratedFile: (filePath: string) =>
filePath.endsWith('.graphql.' + outputExtension) &&
Expand All @@ -272,20 +295,7 @@ Ensure that one such file exists in ${srcDir} or its parents.
// TODO: allow passing in a flag or detect?
sourceControl: null,
});
if (!options.validate && !options.watch && useWatchman) {
// eslint-disable-next-line no-console
console.log('HINT: pass --watch to keep watching for changes.');
}
const result = options.watch
? await codegenRunner.watchAll()
: await codegenRunner.compileAll();

if (result === 'ERROR') {
process.exit(100);
}
if (options.validate && result !== 'NO_CHANGES') {
process.exit(101);
}
return codegenRunner;
}

function getRelayFileWriter(
Expand All @@ -294,6 +304,7 @@ function getRelayFileWriter(
noFutureProofEnums: boolean,
outputDir?: ?string,
persistedQueryPath?: ?string,
customScalars?: ScalarTypeMapping,
) {
return ({
onlyValidate,
Expand Down Expand Up @@ -325,7 +336,7 @@ function getRelayFileWriter(
printTransforms,
queryTransforms,
},
customScalars: {},
customScalars: customScalars || {},
formatModule: languagePlugin.formatModule,
optionalInputFieldsForFlow: [],
schemaExtensions,
Expand Down Expand Up @@ -397,4 +408,4 @@ function hasWatchmanRootFile(testPath) {
return false;
}

module.exports = {main};
module.exports = {getCodegenRunner, main};
Loading

0 comments on commit 553e331

Please sign in to comment.