Skip to content

Commit

Permalink
fix: preserve leading comments
Browse files Browse the repository at this point in the history
  • Loading branch information
homer0 committed Apr 25, 2022
1 parent 0d80372 commit 36c44cc
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 5 deletions.
10 changes: 10 additions & 0 deletions src/transformer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* Some comment about the function.
*/
const path = require('path');
const fs = require('fs-extra');
const { findFileSync, getAbsPathInfoSync } = require('./utils');
Expand Down Expand Up @@ -54,6 +57,8 @@ const transform = (file, api, options) => {
// Generate the list of expressions to ignore import statements.
const ignoreListForExt = cjs2esm.extension.ignore.map((ignore) => new RegExp(ignore));

const originalFirstNode = root.find(j.Program).get('body', 0).node;

// =================================================
// Parse the import statements to add missing extensions.
// =================================================
Expand Down Expand Up @@ -124,6 +129,11 @@ const transform = (file, api, options) => {
});
}

const newFirstNode = root.find(j.Program).get('body', 0).node;
if (newFirstNode !== originalFirstNode) {
newFirstNode.comments = originalFirstNode.comments;
}

// Regenerate the file code.
return root.toSource({
quote: 'single',
Expand Down
139 changes: 134 additions & 5 deletions tests/transformer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ describe('transformer', () => {
];
let currentNodes = nodes.slice();
const message = 'done';
const firstNode = 'first-node';
const programUtils = {
get: jest.fn(() => firstNode),
};
const ast = {
filter: jest.fn(),
replaceWith: jest.fn(),
find: jest.fn(() => ast),
find: jest.fn((type) => (type === 'Program' ? programUtils : ast)),
toSource: jest.fn(() => message),
};
ast.filter.mockImplementationOnce((fn) => {
Expand All @@ -61,6 +65,7 @@ describe('transformer', () => {
});
const jscodeshift = jest.fn(() => ast);
jscodeshift.ImportDeclaration = 'ImportDeclaration';
jscodeshift.Program = 'Program';
jscodeshift.importDeclaration = jest.fn((_, str) => str);
jscodeshift.literal = jest.fn((str) => str);
const api = { jscodeshift };
Expand Down Expand Up @@ -90,6 +95,110 @@ describe('transformer', () => {
'specifier',
'./some/file.js',
);
expect(ast.find).toHaveBeenCalledTimes(3);
expect(ast.find).toHaveBeenNthCalledWith(1, 'Program');
expect(ast.find).toHaveBeenNthCalledWith(2, 'ImportDeclaration');
expect(ast.find).toHaveBeenNthCalledWith(3, 'Program');
expect(programUtils.get).toHaveBeenCalledTimes(2);
expect(programUtils.get).toHaveBeenNthCalledWith(1, 'body', 0);
expect(programUtils.get).toHaveBeenNthCalledWith(2, 'body', 0);
});

it('should transform a file and preserve the leading comments', () => {
// Given
const file = {
path: path.join(cwd, 'index.js'),
source: 'magic',
};
const nodes = [
{
value: {
specifiers: 'specifier',
source: {
value: './some/file',
},
},
},
{
value: {
specifiers: 'specifier',
source: {
value: '~/some/weird/import/that/will/be/ignored',
},
},
},
];
let currentNodes = nodes.slice();
const message = 'done';
const originalFirstNode = {
node: {
comments: 'original-first-node-comments',
},
};
const newFirstNode = {
node: {
comments: 'new-first-node-comments',
},
};
let getFirstNodeCount = 0;
const programUtils = {
get: jest.fn(() => {
const result = getFirstNodeCount ? newFirstNode : originalFirstNode;
getFirstNodeCount++;
return result;
}),
};
const ast = {
filter: jest.fn(),
replaceWith: jest.fn(),
find: jest.fn((type) => (type === 'Program' ? programUtils : ast)),
toSource: jest.fn(() => message),
};
ast.filter.mockImplementationOnce((fn) => {
currentNodes = currentNodes.filter(fn);
return ast;
});
ast.filter.mockImplementationOnce((fn) => {
currentNodes = currentNodes.filter(fn);
return ast;
});
ast.replaceWith.mockImplementationOnce((fn) => {
currentNodes = currentNodes.map(fn);
return ast;
});
const jscodeshift = jest.fn(() => ast);
jscodeshift.ImportDeclaration = 'ImportDeclaration';
jscodeshift.Program = 'Program';
jscodeshift.importDeclaration = jest.fn((_, str) => str);
jscodeshift.literal = jest.fn((str) => str);
const api = { jscodeshift };
const cjs2esm = {
extension: {
ignore: [],
},
modules: [],
};
const options = { cjs2esm };
utils.getAbsPathInfoSync.mockImplementationOnce(() => ({
isFile: true,
path: path.join(cwd, 'some', 'file.js'),
}));
let result = null;
// When
result = transformer(file, api, options);
// Then
expect(result).toBe(message);
expect(currentNodes).toEqual(['./some/file.js']);
expect(jscodeshift).toHaveBeenCalledTimes(1);
expect(jscodeshift).toHaveBeenCalledWith(file.source);
expect(utils.getAbsPathInfoSync).toHaveBeenCalledTimes(1);
expect(utils.getAbsPathInfoSync).toHaveBeenCalledWith(path.join(cwd, 'some', 'file'));
expect(jscodeshift.importDeclaration).toHaveBeenCalledTimes(1);
expect(jscodeshift.importDeclaration).toHaveBeenCalledWith(
'specifier',
'./some/file.js',
);
expect(newFirstNode.comments).toBe(originalFirstNode.comments);
});

it('should transform a file that imports a directories', () => {
Expand Down Expand Up @@ -126,10 +235,14 @@ describe('transformer', () => {
];
let currentNodes = nodes.slice();
const message = 'done';
const firstNode = 'first-node';
const programUtils = {
get: jest.fn(() => firstNode),
};
const ast = {
filter: jest.fn(),
replaceWith: jest.fn(),
find: jest.fn(() => ast),
find: jest.fn((type) => (type === 'Program' ? programUtils : ast)),
toSource: jest.fn(() => message),
};
ast.filter.mockImplementationOnce((fn) => {
Expand All @@ -146,6 +259,7 @@ describe('transformer', () => {
});
const jscodeshift = jest.fn(() => ast);
jscodeshift.ImportDeclaration = 'ImportDeclaration';
jscodeshift.Program = 'Program';
jscodeshift.importDeclaration = jest.fn((_, str) => str);
jscodeshift.literal = jest.fn((str) => str);
const api = { jscodeshift };
Expand Down Expand Up @@ -231,10 +345,14 @@ describe('transformer', () => {
];
let currentNodes = nodes.slice();
const message = 'done';
const firstNode = 'first-node';
const programUtils = {
get: jest.fn(() => firstNode),
};
const ast = {
filter: jest.fn(),
replaceWith: jest.fn(),
find: jest.fn(() => ast),
find: jest.fn((type) => (type === 'Program' ? programUtils : ast)),
toSource: jest.fn(() => message),
};
ast.filter.mockImplementationOnce((fn) => {
Expand All @@ -251,6 +369,7 @@ describe('transformer', () => {
});
const jscodeshift = jest.fn(() => ast);
jscodeshift.ImportDeclaration = 'ImportDeclaration';
jscodeshift.Program = 'Program';
jscodeshift.importDeclaration = jest.fn((_, str) => str);
jscodeshift.literal = jest.fn((str) => str);
const api = { jscodeshift };
Expand Down Expand Up @@ -331,10 +450,14 @@ describe('transformer', () => {
},
];
let currentNodes = nodes.slice();
const firstNode = 'first-node';
const programUtils = {
get: jest.fn(() => firstNode),
};
const ast = {
filter: jest.fn(),
replaceWith: jest.fn(),
find: jest.fn(() => ast),
find: jest.fn((type) => (type === 'Program' ? programUtils : ast)),
toSource: jest.fn(),
};
ast.filter.mockImplementationOnce((fn) => {
Expand All @@ -351,6 +474,7 @@ describe('transformer', () => {
});
const jscodeshift = jest.fn(() => ast);
jscodeshift.ImportDeclaration = 'ImportDeclaration';
jscodeshift.Program = 'Program';
jscodeshift.importDeclaration = jest.fn((_, str) => str);
jscodeshift.literal = jest.fn((str) => str);
const api = { jscodeshift };
Expand Down Expand Up @@ -416,10 +540,14 @@ describe('transformer', () => {
];
let currentNodes = nodes.slice();
const message = 'done';
const firstNode = 'first-node';
const programUtils = {
get: jest.fn(() => firstNode),
};
const ast = {
filter: jest.fn(),
replaceWith: jest.fn(),
find: jest.fn(() => ast),
find: jest.fn((type) => (type === 'Program' ? programUtils : ast)),
toSource: jest.fn(() => message),
};
ast.filter.mockImplementationOnce((fn) => {
Expand Down Expand Up @@ -452,6 +580,7 @@ describe('transformer', () => {
});
const jscodeshift = jest.fn(() => ast);
jscodeshift.ImportDeclaration = 'ImportDeclaration';
jscodeshift.Program = 'Program';
jscodeshift.importDeclaration = jest.fn((_, str) => str);
jscodeshift.literal = jest.fn((str) => str);
const api = { jscodeshift };
Expand Down

0 comments on commit 36c44cc

Please sign in to comment.