@samitouri / QOS-React-2 / commits / f3271ad53b

Added jsx_orphaned_brackets_transformer for breaking JSX parser change

syranide committed Feb 2, 2015 at 23:24 UTC f3271ad53b2476b25532a5ebc206e81f0afefdc1
5 files changed +271
npm-jsx_orphaned_brackets_transformer/.gitignore new
+1
@@ -0,0 +1 @@
1 +node_modules
npm-jsx_orphaned_brackets_transformer/README.md new
+31
@@ -0,0 +1,31 @@
1 +# JSX Orphaned Brackets Transformer
2 +
3 +React 0.13 no longer parses orphaned > and } as text.
4 +
5 +Take this example block:
6 +
7 +```js
8 +<div>
9 + > }
10 +</div>
11 +```
12 +
13 +In 0.12 and below, this would be transformed to the following:
14 +
15 +```js
16 +React.DOM.div(null,
17 + "> }",
18 +)
19 +```
20 +
21 +In 0.13, this will instead throw a parser error.
22 +
23 +
24 +## Usage
25 +
26 +The `jsx_orphaned_brackets_transformer` module ships an executable which transforms a file or directory of files. Files will be modified in place, so be sure you are prepared for that.
27 +
28 +```sh
29 +$ npm -g install jsx_orphaned_brackets_transformer
30 +$ jsx_orphaned_brackets_transformer <path_to_file_or_files>
31 +```
npm-jsx_orphaned_brackets_transformer/package.json new
+18
@@ -0,0 +1,18 @@
1 +{
2 + "name": "jsx_orphaned_brackets_transformer",
3 + "description": "A utility to update your JSX to work with React 0.13.",
4 + "version": "1.0.0",
5 + "main": "run.js",
6 + "dependencies": {
7 + "esprima-fb": "10001.1.0-dev-harmony-fb",
8 + "graceful-fs": "~2.0.0",
9 + "jstransform": "~2.0.1",
10 + "node-find-files": "0.0.2",
11 + "optimist": "~0.6.0"
12 + },
13 + "bin": {
14 + "jsx_orphaned_brackets_transformer": "./run.js"
15 + },
16 + "license": "Apache-2.0",
17 + "preferGlobal": true
18 +}
npm-jsx_orphaned_brackets_transformer/run.js new
+163
@@ -0,0 +1,163 @@
1 +#!/usr/bin/env node
2 +
3 +var esprima = require('esprima-fb');
4 +var FileFinder = require('node-find-files');
5 +var fs = require('graceful-fs');
6 +var jstransform = require('jstransform');
7 +var path = require('path');
8 +var visitReactTag = require('./transforms/react').visitReactTag;
9 +
10 +var S = esprima.Syntax;
11 +
12 +var USAGE =
13 + 'Read a file (or directory of files) from disk, transform any orphaned ' +
14 + '} and > characters to avoid parser errors with React 0.13.';
15 +
16 +function _visitFbt(node, path, state) {
17 + return false;
18 +}
19 +_visitFbt.test = function(node, path, state) {
20 + return node.type === S.XJSElement
21 + && node.openingElement.name.name === 'fbt';
22 +};
23 +
24 +var VISITORS_LIST = [
25 + _visitFbt,
26 + visitReactTag
27 +];
28 +
29 +function _transformSource(source) {
30 + return jstransform.transform(VISITORS_LIST, source).code;
31 +}
32 +
33 +function transformDir(dirPath, exclude) {
34 + var finder = new FileFinder({
35 + rootFolder: dirPath,
36 + filterFunction: function(path, stat) {
37 + return /\.jsx?$/.test(path) && (!exclude || !exclude.test(path));
38 + }
39 + });
40 +
41 + var numTransforms = 0;
42 + var completeTransforms = 0;
43 + var findingComplete = false;
44 + function _printProgress() {
45 + process.stdout.clearLine();
46 + process.stdout.cursorTo(0);
47 + process.stdout.write(
48 + completeTransforms + '/' + numTransforms + ' transforms complete'
49 + );
50 +
51 + if (findingComplete && completeTransforms === numTransforms) {
52 + console.log('\ndone!');
53 + }
54 + }
55 +
56 + finder.on('match', function(pathStr, stat) {
57 + fs.readFile(pathStr, 'utf8', function(err, data) {
58 + if (err) {
59 + err.message = err.message + ' (' + pathStr + ')';
60 + throw err;
61 + }
62 +
63 + numTransforms++;
64 + _printProgress();
65 +
66 + var transformedData;
67 + try {
68 + transformedData = _transformSource(data);
69 + } catch (e) {
70 + e.message = e.message + ' (' + pathStr + ')';
71 + throw e;
72 + }
73 +
74 + if (transformedData !== data) {
75 + fs.writeFile(pathStr, transformedData, function(err) {
76 + if (err) {
77 + err.message = err.message + ' (' + pathStr + ')';
78 + throw err;
79 + }
80 + completeTransforms++;
81 + _printProgress();
82 + });
83 + } else {
84 + completeTransforms++;
85 + _printProgress();
86 + }
87 + });
88 + });
89 +
90 + finder.on('error', function(err) {
91 + console.log('\nError: ', err.stack);
92 + throw err;
93 + });
94 +
95 + finder.on('complete', function() {
96 + findingComplete = true;
97 + });
98 +
99 + finder.startSearch();
100 +}
101 +
102 +function transformFile(pathStr) {
103 + fs.readFile(pathStr, 'utf8', function(err, data) {
104 + if (err) {
105 + err.message = err.message + ' (' + pathStr + ')';
106 + throw err;
107 + }
108 +
109 + var transformedData;
110 + try {
111 + transformedData = _transformSource(data);
112 + } catch (e) {
113 + e.message = e.message + ' (' + pathStr + ')';
114 + throw e;
115 + }
116 +
117 + if (transformedData !== data) {
118 + fs.writeFile(pathStr, transformedData, function(err) {
119 + if (err) {
120 + err.message = err.message + ' (' + pathStr + ')';
121 + throw err;
122 + }
123 + console.log('done!');
124 + });
125 + } else {
126 + console.log('done!');
127 + }
128 + });
129 +}
130 +
131 +if (require.main === module) {
132 + var argv = require('optimist')
133 + .usage(USAGE)
134 + .argv;
135 +
136 + if (argv._.length === 0) {
137 + throw new Error(
138 + 'Please specify a file or directory path as the first arg!'
139 + );
140 + }
141 +
142 + argv._.forEach(function(arg) {
143 + var absPath = path.resolve(arg);
144 +
145 + fs.stat(absPath, function(err, stat) {
146 + if (err) throw err;
147 +
148 + if (stat.isFile()) {
149 + transformFile(absPath);
150 + } else if (stat.isDirectory()) {
151 + var exclude = null;
152 + if (argv.exclude) {
153 + exclude = new RegExp(argv.exclude);
154 + }
155 + transformDir(absPath, exclude);
156 + } else {
157 + throw new Error('Unknown filesystem node type: ' + absPath);
158 + }
159 + });
160 + });
161 +}
162 +
163 +exports.transformDir = transformDir;
npm-jsx_orphaned_brackets_transformer/transforms/react.js new
+58
@@ -0,0 +1,58 @@
1 +/**
2 + * Copyright 2013-2014 Facebook, Inc.
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +/*global exports:true*/
17 +"use strict";
18 +var Syntax = require('esprima-fb').Syntax;
19 +var utils = require('jstransform/src/utils');
20 +
21 +function visitReactTag(traverse, object, path, state) {
22 + object.attributes.forEach(function(attr, index) {
23 + if (attr.value) {
24 + traverse(attr.value, path, state);
25 + }
26 + });
27 +
28 + object.children.forEach(function(child, index) {
29 + if (child.type === Syntax.Literal) {
30 + codemodXJSLiteral(child, state);
31 + } else {
32 + traverse(child, path, state);
33 + }
34 + });
35 +
36 + return false;
37 +}
38 +
39 +visitReactTag.test = function(object, path, state) {
40 + return object.type === Syntax.XJSElement;
41 +};
42 +
43 +function codemodXJSLiteral(object, state) {
44 + var value = object.raw;
45 +
46 + utils.catchup(object.range[0], state);
47 +
48 + var rightCurlyBracket = '&#125;'; // or {'}'}
49 + var rightAngledBracket = '&gt;'; // or {'>'}
50 +
51 + value = value.replace(/\}/g, rightCurlyBracket);
52 + value = value.replace(/\>/g, rightAngledBracket);
53 +
54 + utils.append(value, state);
55 + utils.move(object.range[1], state);
56 +}
57 +
58 +exports.visitReactTag = visitReactTag;