Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improves comment handling in CommonJS import transformation #41

Merged
merged 1 commit into from
May 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ Our transforms will automatically distinguish and pass through Recast config key

## Known issues

* Currently loses comments if directly before the `require()` statement.
* `require()` calls in single var statements get reordered, and moved before the single var after conversion to import.
* Can't automagically figure out when you want to use `import * as varName`.
* End-of-line comments also missing in many situations
* `simple-arrow` loses comments in the function expression body
21 changes: 21 additions & 0 deletions test/fixtures/cjs/comments.after.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* External dependencies
*/
import React from 'react';

import SomeImport from 'some-import/main';
import omit from 'lodash/omit';

/**
* Internal dependencies
*/
import Fetcher from 'components/list-fetcher';

import Page from './page';
import infiniteScroll from 'mixins/infinite-scroll';
import observe from 'mixins/observe';
import EmptyContent from 'components/empty-content';
import NoResults from 'no-results';
import actions from 'actions';
import Placeholder from './placeholder';
import { mapPostStatus as mapStatus } from 'lib/route';
19 changes: 19 additions & 0 deletions test/fixtures/cjs/comments.before.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* External dependencies
*/
var React = require( 'react' ),
SomeImport = require( 'some-import/main' ),
omit = require( 'lodash/omit' );

/**
* Internal dependencies
*/
var Fetcher = require( 'components/list-fetcher' ),
Page = require( './page' ),
infiniteScroll = require( 'mixins/infinite-scroll' ),
observe = require( 'mixins/observe' ),
EmptyContent = require( 'components/empty-content' ),
NoResults = require( 'no-results' ),
actions = require( 'actions' ),
Placeholder = require( './placeholder' ),
mapStatus = require( 'lib/route' ).mapPostStatus;
1 change: 1 addition & 0 deletions test/transforms/cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('CJS transform', function() {
it('var ... = require("y")( ... )', helper.bind(this, 'called'))
it('var x = { x: require("x"), y: require("y"), ... }', helper.bind(this, 'mapper'))
it('should ignore requires deepr than top-level', helper.bind(this, 'ignore'))
it('should preserve comments before and after require\'s', helper.bind(this, 'comments'))
});

function helper (name) {
Expand Down
44 changes: 28 additions & 16 deletions transforms/cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* cjs - Replace require() calls with es6 imports statements
*/

var sortBy = require('lodash/sortBy');
var util = require('../utils/main');

module.exports = function transformer(file, api, options) {
Expand All @@ -13,15 +14,21 @@ module.exports = function transformer(file, api, options) {
.forEach(function (expressionStatement) {
if (!isParentRoot(expressionStatement)) return
j(expressionStatement).replaceWith(convertRequire(expressionStatement, expressionStatement.node.comments));
})
});

// var ... = require('y')
root.find(j.VariableDeclarator, { init: { callee: { name: 'require' }}})
.forEach(replaceDeclarator.bind(undefined, j))
root.find(j.VariableDeclaration).forEach(function(variableDeclaration) {
// var ... = require('y')
var defaultImports = j(variableDeclaration).find(j.VariableDeclarator, { init: { callee: { name: 'require' }}});

// var ... = require('y').x
root.find(j.VariableDeclarator, { init: { object: { callee: { name: 'require' }}}})
.forEach(replaceDeclarator.bind(undefined, j))
// var ... = require('y').x
var namedImports = j(variableDeclaration).find(j.VariableDeclarator, { init: { object: { callee: { name: 'require' }}}});

var sortedImports = sortBy(
defaultImports.paths().concat(namedImports.paths()),
['value.loc.start.line', 'value.loc.start.column']
);
sortedImports.forEach(replaceDeclarator.bind(undefined, j))
});

// var x = { x: require('...'), y: require('...'), ... }
root.find(j.VariableDeclaration, { declarations: [{ init: { type: 'ObjectExpression' }}] })
Expand All @@ -43,8 +50,8 @@ module.exports = function transformer(file, api, options) {

j(variableDeclaration).insertBefore(importStatement)
j(property).replaceWith(newProp)
})
})
});
});

// var x = require("y")( ... )
root.find(j.VariableDeclarator, {
Expand All @@ -65,7 +72,7 @@ module.exports = function transformer(file, api, options) {
}

function isParentRoot(path) {
return path.parent.node.type === 'Program'
return path.parent.node.type === 'Program';
}

function convertRequire (ast, comments) {
Expand All @@ -78,20 +85,25 @@ function convertRequire (ast, comments) {
);
}

function replaceDeclarator (j, variableDeclarator) {
function replaceDeclarator (j, variableDeclarator, index) {
var variableDeclaration = variableDeclarator.parent
var variableDeclarationComments = Array.isArray(variableDeclaration.node.comments) ? variableDeclaration.node.comments : []
if (!isParentRoot(variableDeclaration)) return

// create unique variableDeclaration for each declarator (for more consistent prop extraction)
var varStatement = j.variableDeclaration('var', [variableDeclarator.node]);
var isSingleDeclarator = variableDeclaration.node.declarations.length === 1

if (isSingleDeclarator) {
j(variableDeclaration).replaceWith(convertRequire(varStatement, variableDeclaration.node.comments))
var isLastDeclarator = variableDeclaration.node.declarations.length === 1
var isFirstDeclarator = index === 0;
var comments = variableDeclarationComments.filter(function(comment){
return (isFirstDeclarator && comment.leading) || (isLastDeclarator && comment.trailing);
})

if (isLastDeclarator) {
j(variableDeclaration).replaceWith(convertRequire(varStatement, comments));
} else {
// HACK: Using before for now, to avoid it mangling the whitespace after the var statement.
// This will cause problems if the single var statement contains deps that the other els depend on
j(variableDeclaration).insertBefore(convertRequire(varStatement));
j(variableDeclaration).insertBefore(convertRequire(varStatement, comments));
j(variableDeclarator).remove();
}
}
Expand Down