tessellate-transform
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -1,6 +0,16 @@ | ||
<a name="1.0.0"></a> | ||
# 1.0.0 (2016-11-22) | ||
<a name="1.1.0"></a> | ||
# 1.1.0 (2016-11-23) | ||
<a name="tessellate-transform@1.1.0"></a> | ||
# tessellate-transform@1.1.0 (2016-11-23) | ||
### Features | ||
* **transform:** Add JSON support. ([06f96a3](https://github.com/zalando-incubator/tessellate/commit/06f96a3)) | ||
<a name="tessellate-transform@1.0.0"></a> | ||
@@ -7,0 +17,0 @@ # tessellate-transform@1.0.0 (2016-11-22) |
351
dist/cli.js
@@ -65,3 +65,3 @@ module.exports = | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 11); | ||
/******/ return __webpack_require__(__webpack_require__.s = 14); | ||
/******/ }) | ||
@@ -79,4 +79,4 @@ /************************************************************************/ | ||
}); | ||
exports.default = parse; | ||
const acorn = __webpack_require__(2); | ||
exports.default = createParser; | ||
const acorn = __webpack_require__(5); | ||
@@ -110,4 +110,2 @@ function processAttribute(attribute) { | ||
} | ||
default: | ||
result = null; | ||
} | ||
@@ -117,72 +115,185 @@ return { [attribute.name.name]: result }; | ||
function processExpression(expression, opts) { | ||
function processExpression(expression, callbacks) { | ||
if (expression.type === 'JSXElement') { | ||
opts.onEnter(expression.openingElement); | ||
callbacks.onEnter(expression.openingElement); | ||
for (let child of expression.children) { | ||
processExpression(child, opts, expression); | ||
processExpression(child, callbacks); | ||
} | ||
opts.onLeave(expression.openingElement); | ||
callbacks.onLeave(expression.openingElement); | ||
} | ||
if (expression.type === 'Literal') { | ||
opts.onLiteral(expression); | ||
callbacks.onLiteral(expression); | ||
} | ||
} | ||
function traverseAST(ast, opts) { | ||
function traverseAST(ast, callbacks) { | ||
for (let statement of ast.body) { | ||
const expression = statement.expression; | ||
processExpression(expression, opts); | ||
processExpression(expression, callbacks); | ||
} | ||
} | ||
function parse(jsx, opts) { | ||
function parseElementType(element, typePrefix) { | ||
if (element.name.type === 'JSXIdentifier') { | ||
const name = element.name.name; | ||
const isCustomClassName = /^[A-Z]/.test(name); | ||
return isCustomClassName ? `${ typePrefix }${ name }` : name; | ||
} else if (element.name.type === 'JSXMemberExpression') { | ||
return `${ typePrefix }${ element.name.object.name }.${ element.name.property.name }`; | ||
} else { | ||
throw new Error(`Unsupported element name type ${ element.name.type }`); | ||
} | ||
} | ||
function parseElementAttrs(element) { | ||
return element.attributes.reduce((attrs, attr) => Object.assign(attrs, processAttribute(attr)), {}); | ||
} | ||
function parseElement(element, typePrefix) { | ||
const type = parseElementType(element, typePrefix); | ||
const props = parseElementAttrs(element); | ||
const jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
}; | ||
return jsonNode; | ||
} | ||
function parseLiteral(literal) { | ||
const isNotJustWhitespace = /\S+/.test(literal.raw); | ||
if (isNotJustWhitespace && typeof literal.value === 'string') { | ||
return literal.value; | ||
} | ||
} | ||
class StackCache { | ||
constructor() { | ||
this.cache = []; | ||
this.ids = []; | ||
this.id = 0; | ||
} | ||
push(item) { | ||
this.cache[this.id] = item; | ||
this.ids.push(this.id); | ||
this.id += 1; | ||
return item; | ||
} | ||
pop() { | ||
return this.cache[this.ids.pop()]; | ||
} | ||
} | ||
function parseJSX(jsx, callbacks, opts) { | ||
const ast = acorn.parse(jsx, { plugins: { jsx: true } }); | ||
const typePrefix = opts.typePrefix ? `${ opts.typePrefix }.` : ''; | ||
const ast = acorn.parse(jsx, { plugins: { jsx: true } }); | ||
let root = null; | ||
const nodes = []; | ||
const cache = new StackCache(); | ||
traverseAST(ast, { | ||
onEnter: element => { | ||
let type; | ||
if (element.name.type === 'JSXIdentifier') { | ||
const name = element.name.name; | ||
type = /^[A-Z]/.test(name) ? `${ typePrefix }${ name }` : name; | ||
} else { | ||
type = `${ typePrefix }${ element.name.object.name }.${ element.name.property.name }`; | ||
} | ||
callbacks.onEnter(cache.push(parseElement(element, typePrefix))); | ||
}, | ||
onLeave: element => { | ||
callbacks.onLeave(cache.pop()); | ||
}, | ||
onLiteral: literal => { | ||
const string = parseLiteral(literal); | ||
if (string) callbacks.onLiteral(string); | ||
} | ||
}); | ||
} | ||
const props = element.attributes.reduce((attrs, attr) => Object.assign(attrs, processAttribute(attr)), {}); | ||
const jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
}; | ||
function createParser(opts = {}) { | ||
return (jsx, callbacks) => parseJSX(jsx, callbacks, opts); | ||
} | ||
const lastNode = nodes[nodes.length - 1]; | ||
/***/ }, | ||
/* 1 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
if (lastNode) { | ||
lastNode.children.push(jsonNode); | ||
"use strict"; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = createParser; | ||
var _jsxParser = __webpack_require__(0); | ||
var _jsxParser2 = _interopRequireDefault(_jsxParser); | ||
var _jsonParser = __webpack_require__(4); | ||
var _jsonParser2 = _interopRequireDefault(_jsonParser); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function createParser(type, opts) { | ||
switch (type) { | ||
case '.jsx': | ||
return (0, _jsxParser2.default)(opts); | ||
case '.json': | ||
return (0, _jsonParser2.default)(opts); | ||
default: | ||
throw new Error(`Unsupported type ${ type }`); | ||
} | ||
} | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = transform; | ||
var _jsxParser = __webpack_require__(0); | ||
var _jsxParser2 = _interopRequireDefault(_jsxParser); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function transform(content, parse) { | ||
let root = null; | ||
const nodes = []; | ||
const getLastNode = () => nodes[nodes.length - 1]; | ||
const callbacks = { | ||
onEnter: node => { | ||
const lastNode = getLastNode(); | ||
if (typeof lastNode === 'object') { | ||
lastNode.children.push(node); | ||
} else { | ||
root = jsonNode; | ||
root = node; | ||
} | ||
nodes.push(jsonNode); | ||
nodes.push(node); | ||
}, | ||
onLeave: element => { | ||
onLeave: node => { | ||
nodes.pop(); | ||
}, | ||
onLiteral: literal => { | ||
const lastNode = nodes[nodes.length - 1]; | ||
if (/\S+/.test(literal.raw)) { | ||
lastNode.children.push(literal.value); | ||
const lastNode = getLastNode(); | ||
if (typeof lastNode === 'object') { | ||
lastNode.children.push(literal); | ||
} | ||
} | ||
}); | ||
}; | ||
if (!root) throw new Error('Could not parse JSX.');else return root; | ||
parse(content, callbacks); | ||
if (!root || typeof root !== 'object') throw new Error('Could not parse JSX.');else return root; | ||
} | ||
/***/ }, | ||
/* 1 */ | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -196,29 +307,115 @@ | ||
}); | ||
exports.parse = undefined; | ||
let parse = exports.parse = (() => { | ||
var _transform = __webpack_require__(2); | ||
var _transform2 = _interopRequireDefault(_transform); | ||
var _parsers = __webpack_require__(1); | ||
var _parsers2 = _interopRequireDefault(_parsers); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
exports.default = (() => { | ||
var _ref = _asyncToGenerator(function* (file, opts) { | ||
switch (file.extname) { | ||
case '.jsx': | ||
return (0, _jsxParser2.default)(file.content, opts); | ||
default: | ||
throw new Error(`Unsupported file type ${ file.extname }`); | ||
} | ||
return (0, _transform2.default)(file.content, (0, _parsers2.default)(file.extname, opts)); | ||
}); | ||
return function parse(_x, _x2) { | ||
function parse(_x, _x2) { | ||
return _ref.apply(this, arguments); | ||
}; | ||
} | ||
return parse; | ||
})(); | ||
var _jsxParser = __webpack_require__(0); | ||
/***/ }, | ||
/* 4 */ | ||
/***/ function(module, exports) { | ||
var _jsxParser2 = _interopRequireDefault(_jsxParser); | ||
"use strict"; | ||
'use strict'; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = createParser; | ||
const defaultJsonMap = { | ||
typeKeys: ['type'], | ||
childrenKeys: ['children'], | ||
literalKeys: [], | ||
ignoreKeys: [] | ||
}; | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
function findType(json, typeKeys) { | ||
for (let key of typeKeys) { | ||
if (key in json && typeof json[key] === 'string') { | ||
return json[key]; | ||
} | ||
} | ||
} | ||
function processJSONObject(json, callbacks, opts) { | ||
const jsonMap = opts.jsonMap || defaultJsonMap; | ||
const type = findType(json, jsonMap.typeKeys); | ||
const props = {}; | ||
const childrenAndLiteralKeys = []; | ||
for (let key of Object.keys(json)) { | ||
if (jsonMap.childrenKeys.includes(key)) { | ||
childrenAndLiteralKeys.push(key); | ||
} else if (jsonMap.literalKeys.includes(key)) { | ||
childrenAndLiteralKeys.push(key); | ||
} else if (!jsonMap.ignoreKeys.includes(key) && !jsonMap.typeKeys.includes(key)) { | ||
props[key] = json[key]; | ||
} | ||
} | ||
let jsonNode = null; | ||
if (type) { | ||
jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
}; | ||
} | ||
if (jsonNode) callbacks.onEnter(jsonNode); | ||
for (let key of childrenAndLiteralKeys) { | ||
if (jsonMap.childrenKeys.includes(key) && jsonNode && json[key] && typeof json[key] === 'object') { | ||
parseJSON(json[key], callbacks, opts); | ||
} else if (jsonMap.literalKeys.includes(key) && typeof json[key] === 'string') { | ||
callbacks.onLiteral(json[key]); | ||
} | ||
} | ||
if (jsonNode) callbacks.onLeave(jsonNode); | ||
} | ||
function parseJSON(json, callbacks, opts) { | ||
if (Array.isArray(json)) { | ||
for (let object of json) { | ||
if (object && typeof object === 'object') { | ||
processJSONObject(object, callbacks, opts); | ||
} else if (typeof object === 'string') { | ||
callbacks.onLiteral(object); | ||
} | ||
} | ||
} else { | ||
processJSONObject(json, callbacks, opts); | ||
} | ||
} | ||
function createParser(opts = {}) { | ||
return (json, callbacks) => { | ||
const object = typeof json === 'string' ? JSON.parse(json) : json; | ||
return parseJSON(object, callbacks, opts); | ||
}; | ||
} | ||
/***/ }, | ||
/* 2 */ | ||
/* 5 */ | ||
/***/ function(module, exports) { | ||
@@ -229,12 +426,12 @@ | ||
/***/ }, | ||
/* 3 */ | ||
/* 6 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var Promise = __webpack_require__(7) | ||
var Promise = __webpack_require__(10) | ||
var fs | ||
try { | ||
fs = __webpack_require__(9) | ||
fs = __webpack_require__(12) | ||
} catch(err) { | ||
fs = __webpack_require__(8) | ||
fs = __webpack_require__(11) | ||
} | ||
@@ -278,3 +475,3 @@ | ||
__webpack_require__(10).withCallback(fs, exports, api) | ||
__webpack_require__(13).withCallback(fs, exports, api) | ||
@@ -298,3 +495,3 @@ exports.exists = function (filename, callback) { | ||
/***/ }, | ||
/* 4 */ | ||
/* 7 */ | ||
/***/ function(module, exports) { | ||
@@ -325,3 +522,3 @@ | ||
/***/ }, | ||
/* 5 */ | ||
/* 8 */ | ||
/***/ function(module, exports) { | ||
@@ -332,3 +529,3 @@ | ||
/***/ }, | ||
/* 6 */ | ||
/* 9 */ | ||
/***/ function(module, exports) { | ||
@@ -339,3 +536,3 @@ | ||
/***/ }, | ||
/* 7 */ | ||
/* 10 */ | ||
/***/ function(module, exports) { | ||
@@ -346,3 +543,3 @@ | ||
/***/ }, | ||
/* 8 */ | ||
/* 11 */ | ||
/***/ function(module, exports) { | ||
@@ -353,3 +550,3 @@ | ||
/***/ }, | ||
/* 9 */ | ||
/* 12 */ | ||
/***/ function(module, exports) { | ||
@@ -360,3 +557,3 @@ | ||
/***/ }, | ||
/* 10 */ | ||
/* 13 */ | ||
/***/ function(module, exports) { | ||
@@ -367,3 +564,3 @@ | ||
/***/ }, | ||
/* 11 */ | ||
/* 14 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -405,3 +602,3 @@ | ||
const options = yield parseArgs(args); | ||
const result = yield (0, _.parse)(options.file, options.parser); | ||
const result = yield (0, _2.default)(options.file, options.parser); | ||
console.log(JSON.stringify(result)); | ||
@@ -418,16 +615,18 @@ }); | ||
var _path = __webpack_require__(5); | ||
var _path = __webpack_require__(8); | ||
var _path2 = _interopRequireDefault(_path); | ||
var _fs = __webpack_require__(3); | ||
var _fs = __webpack_require__(6); | ||
var _fs2 = _interopRequireDefault(_fs); | ||
var _yargs = __webpack_require__(6); | ||
var _yargs = __webpack_require__(9); | ||
var _yargs2 = _interopRequireDefault(_yargs); | ||
var _ = __webpack_require__(1); | ||
var _ = __webpack_require__(3); | ||
var _2 = _interopRequireDefault(_); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -441,5 +640,5 @@ | ||
} | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)(module))) | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7)(module))) | ||
/***/ } | ||
/******/ ]); |
@@ -65,3 +65,3 @@ module.exports = | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 1); | ||
/******/ return __webpack_require__(__webpack_require__.s = 3); | ||
/******/ }) | ||
@@ -79,4 +79,4 @@ /************************************************************************/ | ||
}); | ||
exports.default = parse; | ||
const acorn = __webpack_require__(2); | ||
exports.default = createParser; | ||
const acorn = __webpack_require__(5); | ||
@@ -110,4 +110,2 @@ function processAttribute(attribute) { | ||
} | ||
default: | ||
result = null; | ||
} | ||
@@ -117,72 +115,185 @@ return { [attribute.name.name]: result }; | ||
function processExpression(expression, opts) { | ||
function processExpression(expression, callbacks) { | ||
if (expression.type === 'JSXElement') { | ||
opts.onEnter(expression.openingElement); | ||
callbacks.onEnter(expression.openingElement); | ||
for (let child of expression.children) { | ||
processExpression(child, opts, expression); | ||
processExpression(child, callbacks); | ||
} | ||
opts.onLeave(expression.openingElement); | ||
callbacks.onLeave(expression.openingElement); | ||
} | ||
if (expression.type === 'Literal') { | ||
opts.onLiteral(expression); | ||
callbacks.onLiteral(expression); | ||
} | ||
} | ||
function traverseAST(ast, opts) { | ||
function traverseAST(ast, callbacks) { | ||
for (let statement of ast.body) { | ||
const expression = statement.expression; | ||
processExpression(expression, opts); | ||
processExpression(expression, callbacks); | ||
} | ||
} | ||
function parse(jsx, opts) { | ||
function parseElementType(element, typePrefix) { | ||
if (element.name.type === 'JSXIdentifier') { | ||
const name = element.name.name; | ||
const isCustomClassName = /^[A-Z]/.test(name); | ||
return isCustomClassName ? `${ typePrefix }${ name }` : name; | ||
} else if (element.name.type === 'JSXMemberExpression') { | ||
return `${ typePrefix }${ element.name.object.name }.${ element.name.property.name }`; | ||
} else { | ||
throw new Error(`Unsupported element name type ${ element.name.type }`); | ||
} | ||
} | ||
function parseElementAttrs(element) { | ||
return element.attributes.reduce((attrs, attr) => Object.assign(attrs, processAttribute(attr)), {}); | ||
} | ||
function parseElement(element, typePrefix) { | ||
const type = parseElementType(element, typePrefix); | ||
const props = parseElementAttrs(element); | ||
const jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
}; | ||
return jsonNode; | ||
} | ||
function parseLiteral(literal) { | ||
const isNotJustWhitespace = /\S+/.test(literal.raw); | ||
if (isNotJustWhitespace && typeof literal.value === 'string') { | ||
return literal.value; | ||
} | ||
} | ||
class StackCache { | ||
constructor() { | ||
this.cache = []; | ||
this.ids = []; | ||
this.id = 0; | ||
} | ||
push(item) { | ||
this.cache[this.id] = item; | ||
this.ids.push(this.id); | ||
this.id += 1; | ||
return item; | ||
} | ||
pop() { | ||
return this.cache[this.ids.pop()]; | ||
} | ||
} | ||
function parseJSX(jsx, callbacks, opts) { | ||
const ast = acorn.parse(jsx, { plugins: { jsx: true } }); | ||
const typePrefix = opts.typePrefix ? `${ opts.typePrefix }.` : ''; | ||
const ast = acorn.parse(jsx, { plugins: { jsx: true } }); | ||
let root = null; | ||
const nodes = []; | ||
const cache = new StackCache(); | ||
traverseAST(ast, { | ||
onEnter: element => { | ||
let type; | ||
if (element.name.type === 'JSXIdentifier') { | ||
const name = element.name.name; | ||
type = /^[A-Z]/.test(name) ? `${ typePrefix }${ name }` : name; | ||
} else { | ||
type = `${ typePrefix }${ element.name.object.name }.${ element.name.property.name }`; | ||
} | ||
callbacks.onEnter(cache.push(parseElement(element, typePrefix))); | ||
}, | ||
onLeave: element => { | ||
callbacks.onLeave(cache.pop()); | ||
}, | ||
onLiteral: literal => { | ||
const string = parseLiteral(literal); | ||
if (string) callbacks.onLiteral(string); | ||
} | ||
}); | ||
} | ||
const props = element.attributes.reduce((attrs, attr) => Object.assign(attrs, processAttribute(attr)), {}); | ||
const jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
}; | ||
function createParser(opts = {}) { | ||
return (jsx, callbacks) => parseJSX(jsx, callbacks, opts); | ||
} | ||
const lastNode = nodes[nodes.length - 1]; | ||
/***/ }, | ||
/* 1 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
if (lastNode) { | ||
lastNode.children.push(jsonNode); | ||
"use strict"; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = createParser; | ||
var _jsxParser = __webpack_require__(0); | ||
var _jsxParser2 = _interopRequireDefault(_jsxParser); | ||
var _jsonParser = __webpack_require__(4); | ||
var _jsonParser2 = _interopRequireDefault(_jsonParser); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function createParser(type, opts) { | ||
switch (type) { | ||
case '.jsx': | ||
return (0, _jsxParser2.default)(opts); | ||
case '.json': | ||
return (0, _jsonParser2.default)(opts); | ||
default: | ||
throw new Error(`Unsupported type ${ type }`); | ||
} | ||
} | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = transform; | ||
var _jsxParser = __webpack_require__(0); | ||
var _jsxParser2 = _interopRequireDefault(_jsxParser); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function transform(content, parse) { | ||
let root = null; | ||
const nodes = []; | ||
const getLastNode = () => nodes[nodes.length - 1]; | ||
const callbacks = { | ||
onEnter: node => { | ||
const lastNode = getLastNode(); | ||
if (typeof lastNode === 'object') { | ||
lastNode.children.push(node); | ||
} else { | ||
root = jsonNode; | ||
root = node; | ||
} | ||
nodes.push(jsonNode); | ||
nodes.push(node); | ||
}, | ||
onLeave: element => { | ||
onLeave: node => { | ||
nodes.pop(); | ||
}, | ||
onLiteral: literal => { | ||
const lastNode = nodes[nodes.length - 1]; | ||
if (/\S+/.test(literal.raw)) { | ||
lastNode.children.push(literal.value); | ||
const lastNode = getLastNode(); | ||
if (typeof lastNode === 'object') { | ||
lastNode.children.push(literal); | ||
} | ||
} | ||
}); | ||
}; | ||
if (!root) throw new Error('Could not parse JSX.');else return root; | ||
parse(content, callbacks); | ||
if (!root || typeof root !== 'object') throw new Error('Could not parse JSX.');else return root; | ||
} | ||
/***/ }, | ||
/* 1 */ | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -196,29 +307,115 @@ | ||
}); | ||
exports.parse = undefined; | ||
let parse = exports.parse = (() => { | ||
var _transform = __webpack_require__(2); | ||
var _transform2 = _interopRequireDefault(_transform); | ||
var _parsers = __webpack_require__(1); | ||
var _parsers2 = _interopRequireDefault(_parsers); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
exports.default = (() => { | ||
var _ref = _asyncToGenerator(function* (file, opts) { | ||
switch (file.extname) { | ||
case '.jsx': | ||
return (0, _jsxParser2.default)(file.content, opts); | ||
default: | ||
throw new Error(`Unsupported file type ${ file.extname }`); | ||
} | ||
return (0, _transform2.default)(file.content, (0, _parsers2.default)(file.extname, opts)); | ||
}); | ||
return function parse(_x, _x2) { | ||
function parse(_x, _x2) { | ||
return _ref.apply(this, arguments); | ||
}; | ||
} | ||
return parse; | ||
})(); | ||
var _jsxParser = __webpack_require__(0); | ||
/***/ }, | ||
/* 4 */ | ||
/***/ function(module, exports) { | ||
var _jsxParser2 = _interopRequireDefault(_jsxParser); | ||
"use strict"; | ||
'use strict'; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = createParser; | ||
const defaultJsonMap = { | ||
typeKeys: ['type'], | ||
childrenKeys: ['children'], | ||
literalKeys: [], | ||
ignoreKeys: [] | ||
}; | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
function findType(json, typeKeys) { | ||
for (let key of typeKeys) { | ||
if (key in json && typeof json[key] === 'string') { | ||
return json[key]; | ||
} | ||
} | ||
} | ||
function processJSONObject(json, callbacks, opts) { | ||
const jsonMap = opts.jsonMap || defaultJsonMap; | ||
const type = findType(json, jsonMap.typeKeys); | ||
const props = {}; | ||
const childrenAndLiteralKeys = []; | ||
for (let key of Object.keys(json)) { | ||
if (jsonMap.childrenKeys.includes(key)) { | ||
childrenAndLiteralKeys.push(key); | ||
} else if (jsonMap.literalKeys.includes(key)) { | ||
childrenAndLiteralKeys.push(key); | ||
} else if (!jsonMap.ignoreKeys.includes(key) && !jsonMap.typeKeys.includes(key)) { | ||
props[key] = json[key]; | ||
} | ||
} | ||
let jsonNode = null; | ||
if (type) { | ||
jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
}; | ||
} | ||
if (jsonNode) callbacks.onEnter(jsonNode); | ||
for (let key of childrenAndLiteralKeys) { | ||
if (jsonMap.childrenKeys.includes(key) && jsonNode && json[key] && typeof json[key] === 'object') { | ||
parseJSON(json[key], callbacks, opts); | ||
} else if (jsonMap.literalKeys.includes(key) && typeof json[key] === 'string') { | ||
callbacks.onLiteral(json[key]); | ||
} | ||
} | ||
if (jsonNode) callbacks.onLeave(jsonNode); | ||
} | ||
function parseJSON(json, callbacks, opts) { | ||
if (Array.isArray(json)) { | ||
for (let object of json) { | ||
if (object && typeof object === 'object') { | ||
processJSONObject(object, callbacks, opts); | ||
} else if (typeof object === 'string') { | ||
callbacks.onLiteral(object); | ||
} | ||
} | ||
} else { | ||
processJSONObject(json, callbacks, opts); | ||
} | ||
} | ||
function createParser(opts = {}) { | ||
return (json, callbacks) => { | ||
const object = typeof json === 'string' ? JSON.parse(json) : json; | ||
return parseJSON(object, callbacks, opts); | ||
}; | ||
} | ||
/***/ }, | ||
/* 2 */ | ||
/* 5 */ | ||
/***/ function(module, exports) { | ||
@@ -225,0 +422,0 @@ |
@@ -6,5 +6,6 @@ // @flow | ||
import yargs from 'yargs' | ||
import { parse } from './' | ||
import parse from './' | ||
import type { File, ParseOptions } from './' | ||
import type { File } from './' | ||
import type { ParseOptions } from './parsers' | ||
@@ -11,0 +12,0 @@ type Options = {| |
// @flow | ||
import parseJSX from './parsers/jsx-parser' | ||
import transform from './transform' | ||
import createParser from './parsers' | ||
import type { ParseOptions } from './parsers' | ||
export type FileType = '.jsx' | '.json' | '.yaml' | '.xml'; | ||
export type File = {| | ||
content: string; | ||
extname: '.jsx' | '.json' | '.yaml' | '.xml'; | ||
extname: FileType; | ||
|}; | ||
export type ParseOptions = { | ||
typePrefix?: string; | ||
}; | ||
export async function parse(file: File, opts: ParseOptions): Promise<Object> { | ||
switch(file.extname) { | ||
case '.jsx': | ||
return parseJSX(file.content, opts) | ||
default: | ||
throw new Error(`Unsupported file type ${file.extname}`) | ||
} | ||
export default async function parse(file: File, opts: ParseOptions): Promise<*> { | ||
return transform(file.content, createParser(file.extname, opts)) | ||
} |
@@ -5,2 +5,4 @@ // @flow | ||
import type { ParseOptions, ParseResult, ParseCallbacks } from './' | ||
type AST = { | ||
@@ -71,29 +73,8 @@ type: string; | ||
type JsonNode = { | ||
type: string; | ||
props: Object | null; | ||
children: Array<JsonNode>; | ||
}; | ||
type JsonData = { | ||
result: JsonNode | null; | ||
nodes: Array<JsonNode>; | ||
}; | ||
type ProcessingOptions = {| | ||
type ProcessingCallbacks = {| | ||
onEnter: (element: Element) => void; | ||
onLeave: (element: Element) => void; | ||
onLiteral: (literal: Object) => void; | ||
onLiteral: (literal: Literal) => void; | ||
|}; | ||
type ParseOptions = { | ||
typePrefix?: string; | ||
} | ||
type ParseResult = { | ||
type: string; | ||
props: Object | null; | ||
children: Array<ParseResult>; | ||
} | ||
function processAttribute(attribute: Attribute): {[key: string]: mixed} { | ||
@@ -123,4 +104,2 @@ const value = attribute.value | ||
} | ||
default: | ||
result = null | ||
} | ||
@@ -130,69 +109,103 @@ return {[attribute.name.name]: result} | ||
function processExpression(expression: Expression, opts: ProcessingOptions) { | ||
function processExpression(expression: Expression, callbacks: ProcessingCallbacks) { | ||
if (expression.type === 'JSXElement') { | ||
opts.onEnter(expression.openingElement) | ||
callbacks.onEnter(expression.openingElement) | ||
for (let child of expression.children) { | ||
processExpression(child, opts, expression) | ||
processExpression(child, callbacks) | ||
} | ||
opts.onLeave(expression.openingElement) | ||
callbacks.onLeave(expression.openingElement) | ||
} | ||
if (expression.type === 'Literal') { | ||
opts.onLiteral(expression) | ||
callbacks.onLiteral(expression) | ||
} | ||
} | ||
function traverseAST(ast: AST, opts: ProcessingOptions) { | ||
function traverseAST(ast: AST, callbacks: ProcessingCallbacks) { | ||
for (let statement of ast.body) { | ||
const expression = statement.expression; | ||
processExpression(expression, opts) | ||
processExpression(expression, callbacks) | ||
} | ||
} | ||
export default function parse(jsx: string, opts: ParseOptions): ParseResult { | ||
const typePrefix = opts.typePrefix ? `${opts.typePrefix}.` : '' | ||
const ast: AST = acorn.parse(jsx, {plugins: {jsx: true}}) | ||
let root = null | ||
const nodes = [] | ||
function parseElementType(element: Element, typePrefix: string): string { | ||
if (element.name.type === 'JSXIdentifier') { | ||
const name = element.name.name | ||
const isCustomClassName = /^[A-Z]/.test(name) | ||
return isCustomClassName ? `${typePrefix}${name}` : name | ||
} | ||
else if (element.name.type === 'JSXMemberExpression') { | ||
return `${typePrefix}${element.name.object.name}.${element.name.property.name}` | ||
} | ||
else { | ||
throw new Error(`Unsupported element name type ${element.name.type}`) | ||
} | ||
} | ||
traverseAST(ast, { | ||
onEnter: (element: Element) => { | ||
let type | ||
if (element.name.type === 'JSXIdentifier') { | ||
const name = element.name.name | ||
type = /^[A-Z]/.test(name) ? `${typePrefix}${name}` : name | ||
} else { | ||
type = `${typePrefix}${element.name.object.name}.${element.name.property.name}` | ||
} | ||
function parseElementAttrs(element: Element): { [key: string]: mixed } { | ||
return element.attributes.reduce((attrs, attr) => Object.assign(attrs, processAttribute(attr)), {}) | ||
} | ||
const props = element.attributes.reduce((attrs, attr) => Object.assign(attrs, processAttribute(attr)), {}) | ||
const jsonNode = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: [] | ||
} | ||
function parseElement(element: Element, typePrefix: string): ParseResult { | ||
const type = parseElementType(element, typePrefix) | ||
const props = parseElementAttrs(element) | ||
const jsonNode: ParseResult = { | ||
type, | ||
props: Object.keys(props).length > 0 ? props : null, | ||
children: ([]: Array<ParseResult>) | ||
} | ||
return jsonNode | ||
} | ||
const lastNode = nodes[nodes.length - 1] | ||
function parseLiteral(literal: Literal): ?string { | ||
const isNotJustWhitespace = /\S+/.test(literal.raw) | ||
if (isNotJustWhitespace && typeof literal.value === 'string') { | ||
return literal.value | ||
} | ||
} | ||
if (lastNode) { | ||
lastNode.children.push(jsonNode) | ||
} else { | ||
root = jsonNode | ||
} | ||
class StackCache<T> { | ||
cache: Array<T>; | ||
ids: Array<number>; | ||
id: number; | ||
nodes.push(jsonNode) | ||
constructor() { | ||
this.cache = [] | ||
this.ids = [] | ||
this.id = 0 | ||
} | ||
push(item: T): T { | ||
this.cache[this.id] = item | ||
this.ids.push(this.id) | ||
this.id += 1 | ||
return item | ||
} | ||
pop(): T { | ||
return this.cache[this.ids.pop()] | ||
} | ||
} | ||
function parseJSX(jsx: string, callbacks: ParseCallbacks, opts: ParseOptions) { | ||
const ast: AST = acorn.parse(jsx, {plugins: {jsx: true}}) | ||
const typePrefix = opts.typePrefix ? `${opts.typePrefix}.` : '' | ||
const cache = new StackCache() | ||
traverseAST(ast, { | ||
onEnter: (element: Element) => { | ||
callbacks.onEnter(cache.push(parseElement(element, typePrefix))) | ||
}, | ||
onLeave: (element: Element) => { | ||
nodes.pop() | ||
callbacks.onLeave(cache.pop()) | ||
}, | ||
onLiteral: (literal: Object) => { | ||
const lastNode = nodes[nodes.length - 1] | ||
if (/\S+/.test(literal.raw)) { | ||
lastNode.children.push(literal.value) | ||
} | ||
onLiteral: (literal: Literal) => { | ||
const string = parseLiteral(literal) | ||
if (string) callbacks.onLiteral(string) | ||
} | ||
}) | ||
} | ||
if(!root) throw new Error('Could not parse JSX.') | ||
else return root | ||
export default function createParser(opts: ParseOptions = {}) { | ||
return (jsx: string, callbacks: ParseCallbacks) => parseJSX(jsx, callbacks, opts) | ||
} |
{ | ||
"name": "tessellate-transform", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Tessellate JSX/JSON transformer.", | ||
@@ -5,0 +5,0 @@ "author": "Maximilian Fellner <maximilian.fellner@zalando.de>", |
@@ -8,2 +8,4 @@ # tessellate-transform | ||
```javascript | ||
import transform from 'tessellate-transform' | ||
const file = { | ||
@@ -14,3 +16,3 @@ content: '<h1>Hello, world!</h1>', | ||
const options = {} | ||
const object = await parse(file, options) | ||
const object = await transform(file, options) | ||
``` | ||
@@ -17,0 +19,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
172250
13
1218
26