├── .gitignore
├── README.md
├── index.js
├── package.json
└── utils.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # Bower dependency directory (https://bower.io/)
29 | bower_components
30 |
31 | # node-waf configuration
32 | .lock-wscript
33 |
34 | # Compiled binary addons (https://nodejs.org/api/addons.html)
35 | build/Release
36 |
37 | # Dependency directories
38 | node_modules/
39 | jspm_packages/
40 |
41 | # Typescript v1 declaration files
42 | typings/
43 |
44 | # Optional npm cache directory
45 | .npm
46 |
47 | # Optional eslint cache
48 | .eslintcache
49 |
50 | # Optional REPL history
51 | .node_repl_history
52 |
53 | # Output of 'npm pack'
54 | *.tgz
55 |
56 | # Yarn Integrity file
57 | .yarn-integrity
58 |
59 | # dotenv environment variables file
60 | .env
61 |
62 | ### JetBrains template
63 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
64 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
65 |
66 | # User-specific stuff:
67 | .idea/**/workspace.xml
68 | .idea/**/tasks.xml
69 | .idea/dictionaries
70 |
71 | # Sensitive or high-churn files:
72 | .idea/**/dataSources/
73 | .idea/**/dataSources.ids
74 | .idea/**/dataSources.xml
75 | .idea/**/dataSources.local.xml
76 | .idea/**/sqlDataSources.xml
77 | .idea/**/dynamic.xml
78 | .idea/**/uiDesigner.xml
79 |
80 | # Gradle:
81 | .idea/**/gradle.xml
82 | .idea/**/libraries
83 |
84 | # CMake
85 | cmake-build-debug/
86 |
87 | # Mongo Explorer plugin:
88 | .idea/**/mongoSettings.xml
89 |
90 | ## File-based project format:
91 | *.iws
92 |
93 | ## Plugin-specific files:
94 |
95 | # IntelliJ
96 | out/
97 |
98 | # mpeltonen/sbt-idea plugin
99 | .idea_modules/
100 |
101 | # JIRA plugin
102 | atlassian-ide-plugin.xml
103 |
104 | # Cursive Clojure plugin
105 | .idea/replstate.xml
106 |
107 | # Crashlytics plugin (for Android Studio and IntelliJ)
108 | com_crashlytics_export_strings.xml
109 | crashlytics.properties
110 | crashlytics-build.properties
111 | fabric.properties
112 |
113 | .idea/babel-plugin-jsx-if-directive.iml
114 | .idea/inspectionProfiles/
115 | .idea/misc.xml
116 | .idea/modules.xml
117 | .idea/vcs.xml
118 | .idea/watcherTasks.xml
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ⚠️ Not maintained!DO NOT use it on production environment.
2 |
3 | # Using "if" directive in JSX
4 |
5 | [](https://github.com/HuQingyang/babel-plugin-jsx-if-directive)
6 | [](https://github.com/HuQingyang/babel-plugin-jsx-if-directive)
7 | [](https://www.npmjs.com/package/babel-plugin-jsx-if-directive)
8 | [](https://www.npmjs.com/package/babel-plugin-jsx-if-directive)
9 | [](https://www.npmjs.com/package/babel-plugin-jsx-if-directive)
10 |
11 | A easy-to-use "if" directive solution for front-end frameworks using JSX like React.
12 |
13 |
14 | **See Also:**
15 | * Using two-way data binding in JSX: [babel-plugin-jsx-two-way-binding](https://github.com/HuQingyang/babel-plugin-jsx-two-way-binding)
16 | * Using for-directive in JSX: [babel-plugin-jsx-for-directive](https://github.com/HuQingyang/babel-plugin-jsx-for-directive)
17 |
18 |
19 | ## 1. Install
20 | `npm install --save-dev babel-plugin-jsx-if-directive`
21 |
22 | ## 2. Basic Usage
23 | Edit your __.babelrc__ file:
24 | ```json
25 | {
26 | "plugins": [
27 | "jsx-if-directive"
28 | ]
29 | }
30 | ```
31 | In your jsx file:
32 | ```jsx harmony
33 | class App extends Component {
34 | constructor() {
35 | super();
36 | }
37 |
38 | state = {
39 | age: 0
40 | }
41 |
42 | plus = () => {
43 | const { state: { age } } = this;
44 | this.setState({ age: age + 10 });
45 | }
46 |
47 | render() {
48 | const { age } = this.state;
49 | return (
50 |
51 |
52 |
You are child.
53 |
You are youth.
54 |
You are middle-aged.
55 |
You are old man.
56 |
You are {age} years old
57 |
58 | )
59 | }
60 | }
61 | ```
62 |
63 | ## 3. Usage with custom attribute name
64 | Edit your __.babelrc__ file:
65 | ```json
66 | {
67 | "plugins": [
68 | "jsx-if-directive",
69 | {
70 | "ifAttrName": "r-if",
71 | "elseAttrName": "r-else",
72 | "elseIfAttrName": "r-elif"
73 | }
74 | ]
75 | }
76 | ```
77 |
78 | In your jsx file:
79 | ```jsx harmony
80 | class App extends Component {
81 | constructor() {
82 | super();
83 | }
84 |
85 | state = {
86 | age: 0
87 | }
88 |
89 | plus = () => {
90 | const { state: { age } } = this;
91 | this.setState({ age: age + 10 });
92 | }
93 |
94 | render() {
95 | const { age } = this.state;
96 | return (
97 |
98 |
99 |
You are child.
100 |
You are youth.
101 |
You are middle-aged.
102 |
You are old man.
103 |
You are {age} years old
104 |
105 | )
106 | }
107 | }
108 | ```
109 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | const {
3 | removeNode,
4 | findNextNode,
5 | transformIfBinding,
6 | transformElseBinding,
7 | transformElseIfBindings,
8 | getAttrASTAndIndexByName
9 | } = require('./utils');
10 |
11 | const defaultOpts = {
12 | ifAttrName: 'if',
13 | elseAttrName: 'else',
14 | elseIfAttrName: 'elseIf'
15 | };
16 |
17 | module.exports = function () {
18 | function JSXElementVisitor(path, { opts = defaultOpts }) {
19 | const {
20 | ifAttrName,
21 | elseAttrName,
22 | elseIfAttrName
23 | } = opts;
24 |
25 | const ifBinding = getAttrASTAndIndexByName(path.node, ifAttrName);
26 | if (!ifBinding) return;
27 |
28 | let {
29 | parent: { children: siblings },
30 | key: index
31 | } = path;
32 |
33 | let nextNode = findNextNode(path, siblings, index);
34 | if (!nextNode) return transformIfBinding(path, ifBinding);
35 |
36 | let elseBinding = getAttrASTAndIndexByName(nextNode, elseAttrName);
37 | const elseIfBindings = [];
38 |
39 | if (!elseBinding) {
40 | let elseIfBinding = getAttrASTAndIndexByName(nextNode, elseIfAttrName);
41 | while (elseIfBinding) {
42 | elseIfBindings.push(elseIfBinding);
43 | index += 1;
44 | nextNode = findNextNode(path, siblings, index);
45 | elseIfBinding = nextNode ?
46 | getAttrASTAndIndexByName(nextNode, elseIfAttrName) :
47 | null;
48 | }
49 | if (nextNode) {
50 | elseBinding = getAttrASTAndIndexByName(nextNode, elseAttrName);
51 | }
52 | }
53 |
54 | if (elseIfBindings.length > 0) {
55 | transformElseIfBindings(path, ifBinding, elseIfBindings, elseBinding);
56 |
57 | elseIfBindings.forEach((binding) => {
58 | removeNode(siblings, binding.node);
59 | });
60 | if (elseBinding) {
61 | removeNode(siblings, elseBinding.node);
62 | }
63 | } else if (elseBinding) {
64 | transformElseBinding(path, ifBinding, elseBinding);
65 | removeNode(siblings, elseBinding.node);
66 | }
67 | }
68 |
69 | return {
70 | visitor: {
71 | JSXElement: JSXElementVisitor
72 | }
73 | }
74 | };
75 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "babel-plugin-jsx-if-directive",
3 | "version": "0.1.3",
4 | "main": "index.js",
5 | "keywords": [
6 | "JSX",
7 | "React",
8 | "directive",
9 | "if",
10 | "else",
11 | "else if"
12 | ],
13 | "author": "HuQingyang ",
14 | "license": "MIT",
15 | "homepage": "https://github.com/HuQingyang/babel-plugin-jsx-if-directive",
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/HuQingyang/babel-plugin-jsx-if-directive"
19 | },
20 | "dependencies": {
21 | "@babel/types": "^7.0.0-rc.1"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 |
2 | const t = require('@babel/types');
3 |
4 | function findNextNode(path, siblings, index) {
5 | if (!siblings) return null;
6 |
7 | const nextPath = siblings[index + 1];
8 | if (!nextPath) return null;
9 |
10 | const { type, value } = nextPath;
11 | if (type === 'JSXText' && !value.trim())
12 | return findNextNode(nextPath, siblings, index+1);
13 | return type === 'JSXElement' ? nextPath : null;
14 | }
15 |
16 | function getAttrASTAndIndexByName(node, attrName) {
17 | if (!node || !node.openingElement) return null;
18 |
19 | const { type, attributes } = node.openingElement;
20 | if (type !== 'JSXOpeningElement') return null;
21 |
22 | const index = attributes.findIndex(
23 | attr => attr.name && attr.name.name === attrName
24 | );
25 | if (index < 0) return null;
26 |
27 | const attrBinding = attributes[index];
28 | return {
29 | attrBinding,
30 | index,
31 | node
32 | };
33 | }
34 |
35 | function removeNode(nodes, nodeToRemoved) {
36 | const index = nodes.findIndex((i) => i === nodeToRemoved);
37 | if (index >= 0) {
38 | nodes.splice(index, 1);
39 | }
40 | }
41 |
42 | function removeAttrASTByIndex(node, index) {
43 | const { openingElement } = node;
44 | if (!openingElement) return;
45 |
46 | const { attributes } = openingElement;
47 | attributes.splice(index, 1);
48 | }
49 |
50 | function transformIfBinding(path, ifBinding) {
51 | const { attrBinding, index, node } = ifBinding;
52 |
53 | removeAttrASTByIndex(node, index);
54 |
55 | const targetAST = t.conditionalExpression(
56 | attrBinding.value.expression,
57 | node,
58 | t.nullLiteral()
59 | );
60 | path.replaceWith(targetAST);
61 | }
62 |
63 | function transformElseBinding(path, ifBinding, elseBinding) {
64 | const {
65 | attrBinding: ifAttr,
66 | index: ifIndex,
67 | node: ifNode
68 | } = ifBinding;
69 | const {
70 | node: elseNode,
71 | index: elseIndex
72 | } = elseBinding;
73 |
74 | removeAttrASTByIndex(ifNode, ifIndex);
75 | removeAttrASTByIndex(elseNode, elseIndex);
76 |
77 | const targetAST = t.conditionalExpression(
78 | ifAttr.value.expression,
79 | ifNode,
80 | elseNode
81 | );
82 | path.replaceWith(targetAST);
83 | }
84 |
85 | function transformElseIfBindings(path, ifBinding, elseIfBindings, elseBinding) {
86 | const {
87 | attrBinding: ifAttr,
88 | index: ifIndex,
89 | node: ifNode
90 | } = ifBinding;
91 |
92 | removeAttrASTByIndex(ifNode, ifIndex);
93 | if (elseBinding) {
94 | const {
95 | node: elseNode,
96 | index: elseIndex
97 | } = elseBinding;
98 | removeAttrASTByIndex(elseNode, elseIndex);
99 | }
100 | elseIfBindings.forEach((binding) => {
101 | const { node, index } = binding;
102 | removeAttrASTByIndex(node, index);
103 | });
104 |
105 | const callee = t.arrowFunctionExpression([], t.blockStatement([
106 | t.ifStatement(
107 | ifAttr.value.expression,
108 | t.returnStatement(ifNode),
109 | getAlternteAST(elseIfBindings, elseBinding)
110 | )
111 | ]));
112 | const targetAST = t.callExpression(callee, []);
113 | path.replaceWith(targetAST);
114 | }
115 |
116 | function getAlternteAST(elseIfBindings, elseBinding, index=0) {
117 | if (index+1 < elseIfBindings.length) {
118 | const elseIfBinding = elseIfBindings[index];
119 | const {
120 | attrBinding,
121 | node
122 | } = elseIfBinding;
123 | return t.ifStatement(
124 | attrBinding.value.expression,
125 | t.returnStatement(node),
126 | getAlternteAST(elseIfBindings, elseBinding, index+1)
127 | );
128 | }
129 | if (elseBinding) {
130 | return t.returnStatement(elseBinding.node);
131 | }
132 | return null;
133 |
134 | }
135 |
136 | function log(...args) {
137 | args.forEach(i => {
138 | console.log(JSON.stringify(i, '', ' '));
139 | })
140 | }
141 |
142 |
143 | module.exports = {
144 | getAttrASTAndIndexByName,
145 | findNextNode,
146 | transformIfBinding,
147 | transformElseBinding,
148 | removeNode,
149 | transformElseIfBindings,
150 | log
151 | };
152 |
--------------------------------------------------------------------------------