Skip to content
Closed
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
179 changes: 179 additions & 0 deletions packages/react-native-codegen/src/parsers/__tests__/parsers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,97 @@ describe('FlowParser', () => {
).toEqual(expected);
});
});

describe('getTypeArgumentParamsFromDeclaration', () => {
it('returns type arguments params from declaration', () => {
const declaration = {
type: 'TypeAlias',
typeArguments: {
type: 'TypeParameterDeclaration',
params: [
{
type: 'TypeParameter',
name: 'T',
},
],
},
};

const expected = [
{
type: 'TypeParameter',
name: 'T',
},
];

expect(parser.getTypeArgumentParamsFromDeclaration(declaration)).toEqual(
expected,
);
});

it('returns undefined if declaration type argument params is Invalid', () => {
const declaration = {
type: 'TypeAlias',
typeArguments: {
type: 'TypeParameterDeclaration',
},
};

expect(parser.getTypeArgumentParamsFromDeclaration(declaration)).toEqual(
undefined,
);
});
});

describe('getNativeComponentType', () => {
it('returns native component type when typeArgumentParams & funcArgumentParams are valid', () => {
const typeArgumentParams = [
{
type: 'TypeParameter',
name: 'T',
id: {
name: 'T',
},
},
];

const funcArgumentParams = [
{
type: 'StringLiteral',
value: 'componentName',
},
];

const expected = {
propsTypeName: 'T' /* typeArgumentParams[0].id.name */,
componentName: 'componentName' /* funcArgumentParams[0].value */,
};

expect(
parser.getNativeComponentType(typeArgumentParams, funcArgumentParams),
).toEqual(expected);
});

it('returns undefined when typeArgumentParams & funcArgumentParams are invalid', () => {
const typeArgumentParams = [
{
type: 'TypeParameter',
name: 'T',
id: {},
},
];
const funcArgumentParams = [{}];

const expected = {
propsTypeName: undefined /* typeArgumentParams[0].id.name */,
componentName: undefined /* funcArgumentParams[0].value */,
};

expect(
parser.getNativeComponentType(typeArgumentParams, funcArgumentParams),
).toEqual(expected);
});
});
});

describe('TypeScriptParser', () => {
Expand Down Expand Up @@ -326,4 +417,92 @@ describe('TypeScriptParser', () => {
).toEqual(expected);
});
});

describe('getTypeArgumentParamsFromDeclaration', () => {
it('returns type argument params from declaration', () => {
const declaration = {
type: 'TypeAlias',
typeParameters: {
type: 'TypeParameterDeclaration',
params: [
{
type: 'TypeParameter',
name: 'T',
},
],
},
};

const expected = [
{
type: 'TypeParameter',
name: 'T',
},
];

expect(parser.getTypeArgumentParamsFromDeclaration(declaration)).toEqual(
expected,
);
});

it('returns undefined if declaration type arguments params is Invalid', () => {
const declaration = {
type: 'TypeAlias',
typeParameters: {},
};

expect(parser.getTypeArgumentParamsFromDeclaration(declaration)).toEqual(
undefined,
);
});
});

describe('getNativeComponentType', () => {
it('returns native component type when typeArgumentParams & funcArgumentParams are valid', () => {
const typeArgumentParams = [
{
typeName: {
type: 'Identifier',
name: 'T',
},
},
];

const funcArgumentParams = [
{
type: 'ObjectTypeAnnotation',
value: 'StringTypeAnnotation',
},
];

const expected = {
propsTypeName: 'T' /* typeArgumentParams[0].typeName.name */,
componentName: 'StringTypeAnnotation' /* funcArgumentParams[0].value */,
};

expect(
parser.getNativeComponentType(typeArgumentParams, funcArgumentParams),
).toEqual(expected);
});

it('returns undefined when typeArgumentParams & funcArgumentParams are invalid', () => {
const typeArgumentParams = [
{
typeName: {
type: 'Invalid',
},
},
];
const funcArgumentParams = [{}];

const expected = {
propsTypeName: undefined /* typeArgumentParams[0].typeName.name */,
componentName: undefined /* funcArgumentParams[0].value */,
};

expect(
parser.getNativeComponentType(typeArgumentParams, funcArgumentParams),
).toEqual(expected);
});
});
});
40 changes: 9 additions & 31 deletions packages/react-native-codegen/src/parsers/flow/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,22 @@ const {getExtendsProps, removeKnownExtends} = require('./extends');
const {getCommandOptions, getOptions} = require('./options');
const {getProps} = require('./props');
const {getProperties} = require('./componentsUtils.js');
const {createComponentConfig} = require('../../parsers-commons');
const {throwIfMoreThanOneCodegenNativecommands} = require('../../error-utils');
const {
createComponentConfig,
findNativeComponentType,
} = require('../../parsers-commons');

/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
function findComponentConfig(ast) {
const foundConfigs = [];
// $FlowFixMe[signature-verification-failure] there's no flowtype for AST
function findComponentConfig(ast: $FlowFixMe, parser: Parser) {
const foundConfigs: Array<{[string]: string}> = [];

const defaultExports = ast.body.filter(
node => node.type === 'ExportDefaultDeclaration',
);

defaultExports.forEach(statement => {
let declaration = statement.declaration;

// codegenNativeComponent can be nested inside a cast
// expression so we need to go one level deeper
if (declaration.type === 'TypeCastExpression') {
declaration = declaration.expression;
}

try {
if (declaration.callee.name === 'codegenNativeComponent') {
const typeArgumentParams = declaration.typeArguments.params;
const funcArgumentParams = declaration.arguments;

const nativeComponentType: {[string]: string} = {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
if (funcArgumentParams.length > 1) {
nativeComponentType.optionsExpression = funcArgumentParams[1];
}
foundConfigs.push(nativeComponentType);
}
} catch (e) {
// ignore
}
findNativeComponentType(statement, foundConfigs, parser);
});

if (foundConfigs.length === 0) {
Expand Down Expand Up @@ -180,7 +158,7 @@ function buildComponentSchema(
commandTypeName,
commandOptionsExpression,
optionsExpression,
} = findComponentConfig(ast);
} = findComponentConfig(ast, parser);

const types = parser.getTypes(ast);

Expand Down
18 changes: 18 additions & 0 deletions packages/react-native-codegen/src/parsers/flow/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,24 @@ class FlowParser implements Parser {
functionTypeAnnotation(propertyValueType: string): boolean {
return propertyValueType === 'FunctionTypeAnnotation';
}

getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe {
return declaration.typeArguments.params;
}

/**
* This FlowFixMe is supposed to refer to typeArgumentParams and
* funcArgumentParams of generated AST.
*/
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string} {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
}

module.exports = {
Expand Down
18 changes: 18 additions & 0 deletions packages/react-native-codegen/src/parsers/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,22 @@ export interface Parser {
* @returns: a boolean specifying if the property is a function type annotation.
*/
functionTypeAnnotation(propertyValueType: string): boolean;

/**
* Given a declaration, it returns the typeArgumentParams of the declaration.
* @parameter declaration: the declaration.
* @returns: the typeArgumentParams of the declaration.
*/
getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe;

/**
* Given a typeArgumentParams and funcArgumentParams it returns a native component type.
* @parameter typeArgumentParams: the typeArgumentParams.
* @parameter funcArgumentParams: the funcArgumentParams.
* @returns: a native component type.
*/
getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string};
}
14 changes: 14 additions & 0 deletions packages/react-native-codegen/src/parsers/parserMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,18 @@ export class MockedParser implements Parser {
functionTypeAnnotation(propertyValueType: string): boolean {
return propertyValueType === 'FunctionTypeAnnotation';
}

getTypeArgumentParamsFromDeclaration(declaration: $FlowFixMe): $FlowFixMe {
return declaration.typeArguments.params;
}

getNativeComponentType(
typeArgumentParams: $FlowFixMe,
funcArgumentParams: $FlowFixMe,
): {[string]: string} {
return {
propsTypeName: typeArgumentParams[0].id.name,
componentName: funcArgumentParams[0].value,
};
}
}
43 changes: 43 additions & 0 deletions packages/react-native-codegen/src/parsers/parsers-commons.js
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,48 @@ const buildModuleSchema = (
);
};

/**
* This function is used to find the type of a native component
* provided the default exports statement from generated AST.
* @param statement The statement to be parsed.
* @param foundConfigs The 'mutable' array of configs that have been found.
* @param parser The language parser to be used.
* @returns void
*/
function findNativeComponentType(
statement: $FlowFixMe,
foundConfigs: Array<{[string]: string}>,
parser: Parser,
): void {
let declaration = statement.declaration;

// codegenNativeComponent can be nested inside a cast
// expression so we need to go one level deeper
if (
declaration.type === 'TSAsExpression' ||
declaration.type === 'TypeCastExpression'
) {
declaration = declaration.expression;
}

try {
if (declaration.callee.name === 'codegenNativeComponent') {
const typeArgumentParams =
parser.getTypeArgumentParamsFromDeclaration(declaration);
const funcArgumentParams = declaration.arguments;

const nativeComponentType: {[string]: string} =
parser.getNativeComponentType(typeArgumentParams, funcArgumentParams);
if (funcArgumentParams.length > 1) {
nativeComponentType.optionsExpression = funcArgumentParams[1];
}
foundConfigs.push(nativeComponentType);
}
} catch (e) {
// ignore
}
}

module.exports = {
wrapModuleSchema,
unwrapNullable,
Expand All @@ -671,4 +713,5 @@ module.exports = {
createComponentConfig,
parseModuleName,
buildModuleSchema,
findNativeComponentType,
};
Loading