Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[RELEASE]: miniapp complie@0514
  • Loading branch information
imsobear authored May 19, 2020
2 parents 1614f51 + e9d286d commit 6e4da17
Show file tree
Hide file tree
Showing 20 changed files with 789 additions and 83 deletions.
2 changes: 1 addition & 1 deletion packages/jsx-compiler/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jsx-compiler",
"version": "0.4.9",
"version": "0.4.10",
"license": "BSD-3-Clause",
"description": "Parser for Rax JSX Statements.",
"files": [
Expand Down
95 changes: 95 additions & 0 deletions packages/jsx-compiler/src/modules/__tests__/render-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,87 @@ describe('Render item function', () => {
});
});

describe('Render closure function component', () => {
it('should transform basic closure function component', () => {
const ast = parseCode(`
export default function Component() {
const renderItem = () => {
return <View>item</View>;
}
return (
<View>
<View>{renderItem()}</View>
</View>
);
}
`);
const renderFnPath = getDefaultComponentFunctionPath(ast);
const returnPath = getReturnElementPath(ast);
const targetAST = transformReturnPath(returnPath);
const renderItemFunctions = _transformRenderFunction(targetAST, renderFnPath);
expect(renderItemFunctions.map(fn => ({
name: fn.name,
originName: fn.originName
}))).toEqual([{'name': 'renderItemState__temp0', 'originName': 'renderItem'}]);

expect(genExpression(targetAST)).toEqual(`<block a:if="{{$ready}}"><template name="renderItem"><View>item</View></template><View>
<View><template is="renderItem" data="{{...renderItemState__temp0}}"></template></View>
</View></block>`);
});
it('should transform closure function component used function declaration', () => {
const ast = parseCode(`
export default function Component() {
function renderItem() {
return <View>item</View>;
}
return (
<View>
<View>{renderItem()}</View>
</View>
);
}
`);
const renderFnPath = getDefaultComponentFunctionPath(ast);
const returnPath = getReturnElementPath(ast);
const targetAST = transformReturnPath(returnPath);
const renderItemFunctions = _transformRenderFunction(targetAST, renderFnPath);
expect(renderItemFunctions.map(fn => ({
name: fn.name,
originName: fn.originName
}))).toEqual([{'name': 'renderItemState__temp0', 'originName': 'renderItem'}]);

expect(genExpression(targetAST)).toEqual(`<block a:if="{{$ready}}"><template name="renderItem"><View>item</View></template><View>
<View><template is="renderItem" data="{{...renderItemState__temp0}}"></template></View>
</View></block>`);
});
it('should transform closure function component with params', () => {
const ast = parseCode(`
export default function Component() {
const renderItem = (name) => {
return <View>{name}</View>;
}
return (
<View>
<View>{renderItem('rax')}</View>
</View>
);
}
`);
const renderFnPath = getDefaultComponentFunctionPath(ast);
const returnPath = getReturnElementPath(ast);
const targetAST = transformReturnPath(returnPath);
const renderItemFunctions = _transformRenderFunction(targetAST, renderFnPath);
expect(renderItemFunctions.map(fn => ({
name: fn.name,
originName: fn.originName
}))).toEqual([{'name': 'renderItemState__temp0', 'originName': 'renderItem'}]);

expect(genExpression(targetAST)).toEqual(`<block a:if="{{$ready}}"><template name="renderItem"><View>{name}</View></template><View>
<View><template is="renderItem" data="{{...renderItemState__temp0}}"></template></View>
</View></block>`);
});
});

function transformReturnPath(path) {
let returnArgument = path.get('argument').node;
if (!['JSXText', 'JSXExpressionContainer', 'JSXSpreadChild', 'JSXElement', 'JSXFragment'].includes(returnArgument.type)) {
Expand Down Expand Up @@ -74,3 +155,17 @@ function getRenderMethodPath(path) {

return renderMethodPath;
}

function getDefaultComponentFunctionPath(path) {
let defaultComponentFunctionPath = null;
traverse(path, {
ExportDefaultDeclaration(exportDefaultPath) {
const declarationPath = exportDefaultPath.get('declaration');
if (declarationPath.isFunctionDeclaration()) {
defaultComponentFunctionPath = declarationPath;
}
}
});

return defaultComponentFunctionPath;
}
129 changes: 129 additions & 0 deletions packages/jsx-compiler/src/modules/__tests__/render-props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
const t = require('@babel/types');
const traverse = require('../../utils/traverseNodePath');
const { _transformRenderPropsFunction, _renderPropsMap } = require('../render-props');
const { parseCode } = require('../../parser/index');
const getReturnElementPath = require('../../utils/getReturnElementPath');
const adapter = require('../../adapter').ali;
const createJSX = require('../../utils/createJSX');
const createBinding = require('../../utils/createBinding');
const genExpression = require('../../codegen/genExpression');

describe('Pass render props', () => {
it('basic render props usage', () => {
const ast = parseCode(`
export default function Component() {
return (
<View>
<Mouse renderCat={mouse => (
<Cat mouse={mouse} />
)} />
</View>
);
}
`);
const renderFnPath = getDefaultComponentFunctionPath(ast);
const returnPath = getReturnElementPath(ast);
const targetAST = transformReturnPath(returnPath);
const { renderPropsFunctions, renderPropsListener } = _transformRenderPropsFunction(targetAST, renderFnPath);
const [renderClosureFunction, callOnRenderPropsUpdate] = renderPropsListener[0];
expect(renderPropsFunctions.map(fn => ({
name: fn.name,
originName: fn.originName
}))).toEqual([{'name': 'renderCatState__temp0', 'originName': 'renderCat0'}]);
expect(genExpression(renderClosureFunction)).toEqual(`const renderCat0Closure = mouse => ({
mouse: mouse
});`);
expect(genExpression(callOnRenderPropsUpdate)).toEqual(`this._onRenderPropsUpdate("renderCat0", e => {
this._renderCat0 = e;
});`);

expect(genExpression(targetAST)).toEqual(`<block a:if="{{$ready}}"><template name="renderCat0"><Cat mouse={mouse} /></template><View>
<Mouse><view slot="cat"><template is="renderCat0" data="{{...renderCatState__temp0, __tagId: __tagId}}"></template></view></Mouse>
</View></block>`);
});

it('pass a variable declarator of render function to render props', () => {
const ast = parseCode(`
export default function Component() {
const renderCat = (mouse) => {
return (<Cat mouse={mouse} />)
};
return (
<View>
<Mouse renderCat={renderCat} />
</View>
);
}
`);
const renderFnPath = getDefaultComponentFunctionPath(ast);
const returnPath = getReturnElementPath(ast);
const targetAST = transformReturnPath(returnPath);
const { renderPropsFunctions, renderPropsListener } = _transformRenderPropsFunction(targetAST, renderFnPath);
const [renderClosureFunction, callOnRenderPropsUpdate] = renderPropsListener[0];
expect(renderPropsFunctions.map(fn => ({
name: fn.name,
originName: fn.originName
}))).toEqual([{'name': 'renderCatState__temp0', 'originName': 'renderCat0'}]);
expect(genExpression(renderClosureFunction)).toEqual(`const renderCat0Closure = mouse => {
return {
mouse: mouse
};
};`);
expect(genExpression(callOnRenderPropsUpdate)).toEqual(`this._onRenderPropsUpdate("renderCat0", e => {
this._renderCat0 = e;
});`);

expect(genExpression(targetAST)).toEqual(`<block a:if="{{$ready}}"><template name="renderCat0"><Cat mouse={mouse} /></template><View>
<Mouse><view slot="cat"><template is="renderCat0" data="{{...renderCatState__temp0, __tagId: __tagId}}"></template></view></Mouse>
</View></block>`);
});
});

describe('Get render props', () => {
it('basic render props usage', () => {
_renderPropsMap.set('Component_renderCat', 'renderCat0');
const ast = parseCode(`
export default function Component(props) {
const { renderCat } = props;
return (
<View>
{renderCat(name)}
</View>
);
}
`);
const renderFnPath = getDefaultComponentFunctionPath(ast);
const returnPath = getReturnElementPath(ast);
const targetAST = transformReturnPath(returnPath);
const { renderPropsEmitter } = _transformRenderPropsFunction(targetAST, renderFnPath);
expect(genExpression(renderPropsEmitter[0])).toEqual('this._emitRenderPropsUpdate(\"renderCat0\", name);');

expect(genExpression(targetAST)).toEqual(`<block a:if="{{$ready}}"><View>
<slot name="cat"></slot>
</View></block>`);
});
});

function transformReturnPath(path) {
let returnArgument = path.get('argument').node;
if (!['JSXText', 'JSXExpressionContainer', 'JSXSpreadChild', 'JSXElement', 'JSXFragment'].includes(returnArgument.type)) {
returnArgument = t.jsxExpressionContainer(returnArgument);
}
return createJSX('block', {
[adapter.if]: t.stringLiteral(createBinding('$ready')),
}, [returnArgument]);
}

function getDefaultComponentFunctionPath(path) {
let defaultComponentFunctionPath = null;
traverse(path, {
ExportDefaultDeclaration(exportDefaultPath) {
const declarationPath = exportDefaultPath.get('declaration');
if (declarationPath.isFunctionDeclaration()) {
defaultComponentFunctionPath = declarationPath;
}
}
});

return defaultComponentFunctionPath;
}
30 changes: 27 additions & 3 deletions packages/jsx-compiler/src/modules/code.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ module.exports = {
parse(parsed, code, options) {
const { ast, programPath, defaultExportedPath, exportComponentPath, renderFunctionPath,
useCreateStyle, useClassnames, dynamicValue, dynamicRef, dynamicStyle, dynamicEvents, imported,
contextList, refs, componentDependentProps, renderItemFunctions, eventHandler, eventHandlers = [] } = parsed;
contextList, refs, componentDependentProps, renderItemFunctions, renderPropsFunctions, renderPropsEmitter, renderPropsListener, eventHandler, eventHandlers = [] } = parsed;
const { platform, type, cwd, outputPath, sourcePath, resourcePath, disableCopyNpm } = options;
if (type !== 'app' && (!defaultExportedPath || !defaultExportedPath.node)) {
// Can not found default export, otherwise app.js is excluded.
Expand Down Expand Up @@ -181,7 +181,9 @@ module.exports = {
parentNode && parentNode.remove && parentNode.remove();
}
});
addUpdateData(dynamicValue, dynamicRef, dynamicStyle, renderItemFunctions, renderFunctionPath);
addRenderPropsEmitter(renderPropsEmitter, renderFunctionPath);
addRenderPropsListener(renderPropsListener, renderFunctionPath);
addUpdateData(dynamicValue, dynamicRef, dynamicStyle, renderItemFunctions, renderPropsFunctions, renderFunctionPath);
addUpdateEvent(dynamicEvents, eventHandler, renderFunctionPath);
addProviderIniter(contextList, renderFunctionPath);
addRegisterRefs(refs, renderFunctionPath);
Expand Down Expand Up @@ -464,7 +466,26 @@ function collectCoreMethods(raxExported) {
return vaildList;
}

function addUpdateData(dynamicValue, dynamicRef, dynamicStyle, renderItemFunctions, renderFunctionPath) {
function addRenderPropsEmitter(renderPropsEmitter = [], renderFunctionPath) {
if (renderPropsEmitter.length > 0) {
renderPropsEmitter.forEach(emitter => {
renderFunctionPath.node.body.body.push(emitter);
});
}
}

function addRenderPropsListener(renderPropsListener = [], renderFunctionPath) {
if (renderPropsListener.length > 0) {
renderPropsListener.forEach(listener => {
const fnBody = renderFunctionPath.node.body.body;
const [renderClosureFunction, callOnRenderPropsUpdate] = listener;
fnBody.unshift(renderClosureFunction);
fnBody.push(callOnRenderPropsUpdate);
});
}
}

function addUpdateData(dynamicValue, dynamicRef, dynamicStyle, renderItemFunctions, renderPropsFunctions, renderFunctionPath) {
const dataProperties = [];
const dataStore = dynamicValue.getStore();
const refStore = dynamicRef.getStore();
Expand All @@ -476,6 +497,9 @@ function addUpdateData(dynamicValue, dynamicRef, dynamicStyle, renderItemFunctio
renderItemFunctions.map(renderItemFn => {
dataProperties.push(t.objectProperty(t.stringLiteral(renderItemFn.name), renderItemFn.node));
});
renderPropsFunctions.map(renderPropsFn => {
dataProperties.push(t.objectProperty(t.stringLiteral(renderPropsFn.name), renderPropsFn.node));
});

const updateData = t.memberExpression(
t.thisExpression(),
Expand Down
Loading

0 comments on commit 6e4da17

Please sign in to comment.