Skip to content

Commit

Permalink
Merge pull request #1 from ericclemmons/1-release
Browse files Browse the repository at this point in the history
First Release
  • Loading branch information
ericclemmons authored Feb 2, 2017
2 parents d346ffb + db23d96 commit 87112ba
Show file tree
Hide file tree
Showing 39 changed files with 672 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015", "stage-0"]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ coverage

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
dist

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v5.6.0
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# react-transform-functional-hmr
# babel-plugin-react-pure-to-class
https://github.com/gaearon/babel-plugin-react-transform/issues/57#issuecomment-167652102
27 changes: 27 additions & 0 deletions example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --content-base src/public --hot --inline",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"babel-loader": "6.2.2",
"babel-plugin-react-transform": "2.0.0",
"babel-preset-es2015": "6.5.0",
"babel-preset-react": "6.5.0",
"babel-preset-stage-0": "6.5.0",
"react-transform-hmr": "1.0.2",
"webpack": "1.12.13",
"webpack-dev-server": "1.14.1"
},
"dependencies": {
"react": "0.14.7",
"react-dom": "0.14.7"
}
}
8 changes: 8 additions & 0 deletions example/src/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react";
import DOM from "react-dom";

import App from "./components/App";

DOM.render((
<App />,
), document.getElementById("app"));
5 changes: 5 additions & 0 deletions example/src/components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const App = (props) => (
<h1>Howdy!</h1>
);

export default App;
19 changes: 19 additions & 0 deletions example/src/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>babel-plugin-functional-hmr Example</title>
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.6/cerulean/bootstrap.min.css" rel="stylesheet" integrity="sha256-Ucf/ylcKTNevYP6l7VNUhGLDRZPQs1+LsbbxuzMxUJM= sha512-FW2XqnqMwERwg0LplG7D64h8zA1BsxvxrDseWpHLq8Dg8kOBmLs19XNa9oAajN/ToJRRklfDJ398sOU+7LcjZA==" crossorigin="anonymous">
</head>
<body>
<div id="app">
Loading&hellip;
</div>

<script>
document.write("The time is: " + new Date().toString());
</script>

<script src="/client.js"></script>
</body>
</html>
51 changes: 51 additions & 0 deletions example/webpack.config.babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import path from "path";

export default {
entry: {
client: [
"./src/client.js",
]
},

module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
query: {
babelrc: false,
cacheDirectory: true,
passPerPreset: true,
presets: [
// Convert pure to Class
{ plugins: ["../src/plugin.js"] },
// Enhance Classes
{ plugins: [
["react-transform", {
transforms: [{
transform: "react-transform-hmr",
imports: ["react"],
locals: ["module"],
}],
}],
]},
// Convert to ES5
"react",
"es2015",
"stage-0",
],
},
exclude: /node_modules/,
},
],
},

output: {
chunkFilename: "[id].[hash:5]-[chunkhash:7].js",
devtoolModuleFilenameTemplate: "[absolute-resource-path]",
filename: "[name].js",
libraryTarget: "var",
path: path.join(__dirname, "build/client"),
publicPath: "/",
},
};
42 changes: 42 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "babel-plugin-react-pure-to-class",
"version": "1.0.0",
"description": "Convert Functional Components to React Classes for Hot Module Replacement (HMR) & Transforms.",
"main": "dist/plugin.js",
"scripts": {
"build": "babel src -d dist",
"clean": "rimraf dist",
"test": "mocha --compilers js:babel-register",
"watch": "npm test -- --bail --watch"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ericclemmons/babel-plugin-react-pure-to-class.git"
},
"keywords": [
"babel-plugin",
"hmr",
"react",
"functional",
"components"
],
"author": "Eric Clemmons <eric@smarterspam.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/ericclemmons/babel-plugin-react-pure-to-class/issues"
},
"homepage": "https://github.com/ericclemmons/babel-plugin-react-pure-to-class#readme",
"devDependencies": {
"babel-cli": "6.5.1",
"babel-core": "6.5.1",
"babel-plugin-react-transform": "2.0.0",
"babel-plugin-syntax-jsx": "6.5.0",
"babel-preset-es2015": "6.5.0",
"babel-preset-stage-0": "6.5.0",
"babel-register": "6.5.1",
"expect": "1.14.0",
"mocha": "2.4.5",
"react-transform-hmr": "1.0.2",
"rimraf": "2.5.2"
}
}
203 changes: 203 additions & 0 deletions src/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
export default function({ types: t, template }) {
const declareParam = (param, i) => {
if (t.isIdentifier(param)) {
return t.variableDeclaration(
"const",
[
t.variableDeclarator(
t.identifier(param.name),
t.memberExpression(
t.thisExpression(),
t.identifier(param.name),
),
),
],
);
}

if (t.isObjectPattern(param)) {
return t.variableDeclaration(
"const",
[
t.variableDeclarator(
param,
t.memberExpression(
t.thisExpression(),
t.identifier(i === 0 ? "props" : "context"),
),
),
],
)
}
};

const declareParams = (params) => params.map(declareParam);

const functionToClass = (id, path, { file }) => {
const React = findReact(file);

const superClass = t.memberExpression(
t.identifier("React"),
t.identifier("Component"),
);

const decorators = [];
const body = getClassBody(path);

const Component = t.classDeclaration(
id,
superClass,
body,
decorators,
);

return Component;
};

const getClassBody = (path) => {
const body = (t.isBlockStatement(path.node.body))
? path.node.body.body : [t.returnStatement(path.node.body)]
;

const name = t.identifier("render");
const args = [];

const render = t.classMethod(
"method",
name,
args,
t.blockStatement([
...declareParams(path.node.params),
...body,
], [])
);

return t.classBody([
render,
]);
};

const hasJSX = (path) => {
const state = { jsx: false };

path.traverse({
JSXElement(path) {
this.jsx = true;
path.stop();
},
}, state);

return state.jsx;
};

const findReact = (file) => {
const existing = file.path.node.body
.filter((path) => t.isImportDeclaration(path))
.map((path) => path.specifiers
.filter((path) => t.isImportDefaultSpecifier(path))
.map((path) => path.local)
.filter((id) => id.name === "React")
.shift()
)
.shift()
;

if (existing) {
return existing;
}

const React = t.importDeclaration(
[t.importDefaultSpecifier(t.identifier("React"))],
t.stringLiteral("react"),
);

file.path.node.body.unshift(React);

return React;
}

const isCapitalized = (name) => name.match(/^[A-Z]\w+$/);

const FunctionExpression = (path, { file }) => {
if (t.isCallExpression(path.parent)) {
FunctionDeclaration(path, { file });
return;
}

const variable = path.parent;

if (!t.isVariableDeclarator(variable)) {
return;
}

const declaration = path.parentPath.parentPath;

if (!t.isVariableDeclaration(declaration)) {
return;
}

if (!declaration.kind === "const") {
return;
}

const { name } = variable.id;

if (!isCapitalized(name)) {
return;
}

if (!hasJSX(path)) {
return;
}

const id = t.identifier(name);
const Component = functionToClass(id, path, { file });

declaration.replaceWith(Component);
};

const FunctionDeclaration = (path, { file }) => {
const { id } = path.node;

if (!id) {
return;
}

if (!isCapitalized(id.name)) {
return;
}

if (!hasJSX(path)) {
return;
}

const Component = functionToClass(id, path, { file });

if (t.isExportDefaultDeclaration(path.parent)) {
path.parentPath.replaceWith(Component);
path.parentPath.insertAfter(t.exportDefaultDeclaration(id));
} else if (t.isCallExpression(path.parent)) {
if (t.isExportDefaultDeclaration(path.parentPath.parentPath)) {
path.replaceWith(id);
path.parentPath.parentPath.insertBefore(Component);
}
} else if (t.isExportNamedDeclaration(path.parent)) {
path.replaceWith(Component);
} else if (t.isProgram(path.parent)) {
path.replaceWith(Component);
}
};

return {
visitor: {
// const Component = () => {}
ArrowFunctionExpression: FunctionExpression,

// function Component(...)
FunctionDeclaration,

// const Component = function(...)
FunctionExpression,
}
}
}
Loading

0 comments on commit 87112ba

Please sign in to comment.