react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / jstransform / src / jstransform.js
81146 views/**1* Copyright 2013 Facebook, Inc.2*3* Licensed under the Apache License, Version 2.0 (the "License");4* you may not use this file except in compliance with the License.5* You may obtain a copy of the License at6*7* http://www.apache.org/licenses/LICENSE-2.08*9* Unless required by applicable law or agreed to in writing, software10* distributed under the License is distributed on an "AS IS" BASIS,11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12* See the License for the specific language governing permissions and13* limitations under the License.14*/151617/*jslint node: true*/18"use strict";1920var esprima = require('esprima-fb');21var utils = require('./utils');2223var getBoundaryNode = utils.getBoundaryNode;24var declareIdentInScope = utils.declareIdentInLocalScope;25var initScopeMetadata = utils.initScopeMetadata;26var Syntax = esprima.Syntax;2728/**29* @param {object} node30* @param {object} parentNode31* @return {boolean}32*/33function _nodeIsClosureScopeBoundary(node, parentNode) {34if (node.type === Syntax.Program) {35return true;36}3738var parentIsFunction =39parentNode.type === Syntax.FunctionDeclaration40|| parentNode.type === Syntax.FunctionExpression41|| parentNode.type === Syntax.ArrowFunctionExpression;4243return node.type === Syntax.BlockStatement && parentIsFunction;44}4546function _nodeIsBlockScopeBoundary(node, parentNode) {47if (node.type === Syntax.Program) {48return false;49}5051return node.type === Syntax.BlockStatement52&& parentNode.type === Syntax.CatchClause;53}5455/**56* @param {object} node57* @param {array} path58* @param {object} state59*/60function traverse(node, path, state) {61// Create a scope stack entry if this is the first node we've encountered in62// its local scope63var parentNode = path[0];64if (!Array.isArray(node) && state.localScope.parentNode !== parentNode) {65if (_nodeIsClosureScopeBoundary(node, parentNode)) {66var scopeIsStrict =67state.scopeIsStrict68|| node.body.length > 069&& node.body[0].type === Syntax.ExpressionStatement70&& node.body[0].expression.type === Syntax.Literal71&& node.body[0].expression.value === 'use strict';7273if (node.type === Syntax.Program) {74state = utils.updateState(state, {75scopeIsStrict: scopeIsStrict76});77} else {78state = utils.updateState(state, {79localScope: {80parentNode: parentNode,81parentScope: state.localScope,82identifiers: {},83tempVarIndex: 084},85scopeIsStrict: scopeIsStrict86});8788// All functions have an implicit 'arguments' object in scope89declareIdentInScope('arguments', initScopeMetadata(node), state);9091// Include function arg identifiers in the scope boundaries of the92// function93if (parentNode.params.length > 0) {94var param;95for (var i = 0; i < parentNode.params.length; i++) {96param = parentNode.params[i];97if (param.type === Syntax.Identifier) {98declareIdentInScope(99param.name, initScopeMetadata(parentNode), state100);101}102}103}104105// Named FunctionExpressions scope their name within the body block of106// themselves only107if (parentNode.type === Syntax.FunctionExpression && parentNode.id) {108var metaData =109initScopeMetadata(parentNode, path.parentNodeslice, parentNode);110declareIdentInScope(parentNode.id.name, metaData, state);111}112}113114// Traverse and find all local identifiers in this closure first to115// account for function/variable declaration hoisting116collectClosureIdentsAndTraverse(node, path, state);117}118119if (_nodeIsBlockScopeBoundary(node, parentNode)) {120state = utils.updateState(state, {121localScope: {122parentNode: parentNode,123parentScope: state.localScope,124identifiers: {}125}126});127128if (parentNode.type === Syntax.CatchClause) {129declareIdentInScope(130parentNode.param.name, initScopeMetadata(parentNode), state131);132}133collectBlockIdentsAndTraverse(node, path, state);134}135}136137// Only catchup() before and after traversing a child node138function traverser(node, path, state) {139node.range && utils.catchup(node.range[0], state);140traverse(node, path, state);141node.range && utils.catchup(node.range[1], state);142}143144utils.analyzeAndTraverse(walker, traverser, node, path, state);145}146147function collectClosureIdentsAndTraverse(node, path, state) {148utils.analyzeAndTraverse(149visitLocalClosureIdentifiers,150collectClosureIdentsAndTraverse,151node,152path,153state154);155}156157function collectBlockIdentsAndTraverse(node, path, state) {158utils.analyzeAndTraverse(159visitLocalBlockIdentifiers,160collectBlockIdentsAndTraverse,161node,162path,163state164);165}166167function visitLocalClosureIdentifiers(node, path, state) {168var metaData;169switch (node.type) {170case Syntax.FunctionExpression:171// Function expressions don't get their names (if there is one) added to172// the closure scope they're defined in173return false;174case Syntax.ClassDeclaration:175case Syntax.ClassExpression:176case Syntax.FunctionDeclaration:177if (node.id) {178metaData = initScopeMetadata(getBoundaryNode(path), path.slice(), node);179declareIdentInScope(node.id.name, metaData, state);180}181return false;182case Syntax.VariableDeclarator:183// Variables have function-local scope184if (path[0].kind === 'var') {185metaData = initScopeMetadata(getBoundaryNode(path), path.slice(), node);186declareIdentInScope(node.id.name, metaData, state);187}188break;189}190}191192function visitLocalBlockIdentifiers(node, path, state) {193// TODO: Support 'let' here...maybe...one day...or something...194if (node.type === Syntax.CatchClause) {195return false;196}197}198199function walker(node, path, state) {200var visitors = state.g.visitors;201for (var i = 0; i < visitors.length; i++) {202if (visitors[i].test(node, path, state)) {203return visitors[i](traverse, node, path, state);204}205}206}207208var _astCache = {};209210/**211* Applies all available transformations to the source212* @param {array} visitors213* @param {string} source214* @param {?object} options215* @return {object}216*/217function transform(visitors, source, options) {218options = options || {};219var ast;220try {221var cachedAst = _astCache[source];222ast = cachedAst ||223(_astCache[source] = esprima.parse(source, {224comment: true,225loc: true,226range: true227}));228} catch (e) {229e.message = 'Parse Error: ' + e.message;230throw e;231}232var state = utils.createState(source, ast, options);233state.g.visitors = visitors;234235if (options.sourceMap) {236var SourceMapGenerator = require('source-map').SourceMapGenerator;237state.g.sourceMap = new SourceMapGenerator({file: options.filename || 'transformed.js'});238}239240traverse(ast, [], state);241utils.catchup(source.length, state);242243var ret = {code: state.g.buffer, extra: state.g.extra};244if (options.sourceMap) {245ret.sourceMap = state.g.sourceMap;246ret.sourceMapFilename = options.filename || 'source.js';247}248return ret;249}250251exports.transform = transform;252exports.Syntax = Syntax;253254255