Skip to content

Commit

Permalink
Addition of custom collection functions for importDeclarations/Specif…
Browse files Browse the repository at this point in the history
…iers
  • Loading branch information
Daniel Del Core authored and Daniel15 committed Jul 29, 2024
1 parent b54b21c commit 80d46fa
Show file tree
Hide file tree
Showing 3 changed files with 262 additions and 0 deletions.
114 changes: 114 additions & 0 deletions src/collections/ImportDeclaration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

"use strict";

const Collection = require("../Collection");
const NodeCollection = require("./Node");

const assert = require("assert");
const once = require("../utils/once");
const recast = require("recast");

const types = recast.types.namedTypes;

const globalMethods = {
/**
* Inserts an ImportDeclaration at the top of the AST
*
* @param {string} sourcePath
* @param {Array} specifiers
*/
insertImportDeclaration: function (sourcePath, specifiers) {
assert.ok(
sourcePath && typeof sourcePath === "string",
"insertImportDeclaration(...) needs a source path"
);

assert.ok(
specifiers && Array.isArray(specifiers),
"insertImportDeclaration(...) needs an array of specifiers"
);

if (this.hasImportDeclaration(sourcePath)) {
return this;
}

const importDeclaration = recast.types.builders.importDeclaration(
specifiers,
recast.types.builders.stringLiteral(sourcePath)
);

return this.forEach((path) => {
if (path.value.type === "Program") {
path.value.body.unshift(importDeclaration);
}
});
},
/**
* Finds all ImportDeclarations optionally filtered by name
*
* @param {string} sourcePath
* @return {Collection}
*/
findImportDeclarations: function (sourcePath) {
assert.ok(
sourcePath && typeof sourcePath === "string",
"findImportDeclarations(...) needs a source path"
);

return this.find(types.ImportDeclaration, {
source: { value: sourcePath },
});
},

/**
* Determines if the collection has an ImportDeclaration with the given sourcePath
*
* @param {string} sourcePath
* @returns {boolean}
*/
hasImportDeclaration: function (sourcePath) {
assert.ok(
sourcePath && typeof sourcePath === "string",
"findImportDeclarations(...) needs a source path"
);

return this.findImportDeclarations(sourcePath).length > 0;
},

/**
* Renames all ImportDeclarations with the given name
*
* @param {string} sourcePath
* @param {string} newSourcePath
* @return {Collection}
*/
renameImportDeclaration: function (sourcePath, newSourcePath) {
assert.ok(
sourcePath && typeof sourcePath === "string",
"renameImportDeclaration(...) needs a name to look for"
);

assert.ok(
newSourcePath && typeof newSourcePath === "string",
"renameImportDeclaration(...) needs a new name to rename to"
);

return this.findImportDeclarations(sourcePath).forEach((path) => {
path.value.source.value = newSourcePath;
});
},
};

function register() {
NodeCollection.register();
Collection.registerMethods(globalMethods, types.Node);
}

exports.register = once(register);
exports.filters = filterMethods;
147 changes: 147 additions & 0 deletions src/collections/__tests__/ImportDeclaration-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

"use strict";

const getParser = require("./../../getParser");

describe("ImportDeclaration API", function () {
let nodes;
let Collection;
let ImportDeclarationCollection;
let recast;
let types;
let b;

beforeEach(function () {
jest.resetModules();

Collection = require("../../Collection");
ImportDeclarationCollection = require("../ImportDeclaration");
recast = require("recast");
types = recast.types.namedTypes;
b = recast.types.builders;

ImportDeclarationCollection.register();

nodes = [
recast.parse(
[
'import FooBar from "XYZ";',
'import Foo, { Bar, Baz } from "@meta/foo";',
'import { Bar as Burger } from "@meta/bar";',
].join("\n"),
{ parser: getParser() }
).program,
];
});

describe("Traversal", function () {
describe("hasImportDeclaration", function () {
it("returns true if an ImportDeclaration exists", function () {
const hasImport =
Collection.fromNodes(nodes).hasImportDeclaration("XYZ");

expect(hasImport).toBe(true);
});

it("returns false if an ImportDeclaration does not exist", function () {
const hasImport =
Collection.fromNodes(nodes).hasImportDeclaration("ABC");

expect(hasImport).toBe(false);
});
});

describe("findImportDeclarations", function () {
it("lets us find ImportDeclarations by source path conveniently", function () {
const imports =
Collection.fromNodes(nodes).findImportDeclarations("XYZ");

expect(imports.length).toBe(1);
});

it("returns an empty ImportDeclarationCollection if no ImportDeclarations are found", function () {
const imports =
Collection.fromNodes(nodes).findImportDeclarations("Foo");

expect(imports.length).toBe(0);
});
});

describe("renameImportDeclaration", function () {
it("renames an ImportDeclaration with the given sourcePath", function () {
Collection.fromNodes(nodes).renameImportDeclaration("XYZ", "ABC");

{
const imports =
Collection.fromNodes(nodes).findImportDeclarations("ABC");

expect(imports.length).toBe(1);
}
{
const imports =
Collection.fromNodes(nodes).findImportDeclarations("XYZ");
expect(imports.length).toBe(0);
}
});

it("throws if sourcePath is not provided", function () {
expect(function () {
Collection.fromNodes(nodes).renameImportDeclaration();
}).toThrow();
});

it("throws if newSourcePath is not provided", function () {
expect(function () {
Collection.fromNodes(nodes).renameImportDeclaration("XYZ");
}).toThrow();
});
});

describe("insertImportDeclaration", function () {
it("inserts an ImportDeclaration into the AST", function () {
Collection.fromNodes(nodes).insertImportDeclaration("@foo/bar", [
b.importDefaultSpecifier(b.identifier("Foo")),
b.importSpecifier(b.identifier("ABC")),
b.importSpecifier(b.identifier("123")),
]);

const imports =
Collection.fromNodes(nodes).findImportDeclarations("@foo/bar");

expect(imports.length).toBe(1);

console.log(imports.paths()[0].value);
const importSpecifiers = imports.paths()[0].value.specifiers;
expect(importSpecifiers.length).toBe(3);
});

it("does not insert duplicate ImportDeclarations", function () {
Collection.fromNodes(nodes).insertImportDeclaration("@foo/baz", [
b.importDefaultSpecifier(b.identifier("Foo")),
b.importSpecifier(b.identifier("ABC")),
]);

Collection.fromNodes(nodes).insertImportDeclaration("@foo/baz", [
b.importDefaultSpecifier(b.identifier("Foo")),
b.importSpecifier(b.identifier("ABC")),
]);

const imports =
Collection.fromNodes(nodes).findImportDeclarations("@foo/baz");

expect(imports.length).toBe(1);
});

it("throws if importDeclaration is not provided", function () {
expect(function () {
Collection.fromNodes(nodes).insertImportDeclaration();
}).toThrow();
});
});
});
1 change: 1 addition & 0 deletions src/collections/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ module.exports = {
Node: require('./Node'),
JSXElement: require('./JSXElement'),
VariableDeclarator: require('./VariableDeclarator'),
ImportDeclaration: require('./ImportDeclaration'),
};

0 comments on commit 80d46fa

Please sign in to comment.