Skip to content

Commit

Permalink
feat: add support for nested page directories
Browse files Browse the repository at this point in the history
  • Loading branch information
danielcondemarin committed Mar 30, 2019
1 parent 997c312 commit 394a7fc
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 38 deletions.
148 changes: 120 additions & 28 deletions lib/__tests__/getNextPagesFromBuildDir.test.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,70 @@
const fs = require("fs");
const walkDir = require("klaw");
const stream = require("stream");
const path = require("path");
const getNextPagesFromBuildDir = require("../getNextPagesFromBuildDir");
const logger = require("../../utils/logger");

jest.mock("fs");
jest.mock("klaw");
jest.mock("../../utils/logger");

describe("getNextPagesFromBuildDir", () => {
let mockedStream;

beforeEach(() => {
mockedStream = new stream.Readable();
mockedStream._read = () => {};
walkDir.mockReturnValueOnce(mockedStream);
fs.lstatSync.mockReturnValue({ isDirectory: () => false });
});

afterEach(() => {
jest.clearAllMocks();
});

it("should return an empty array when there are no pages", () => {
expect.assertions(1);

fs.readdir.mockImplementationOnce((path, cb) => cb(null, []));
const getPagesPromise = getNextPagesFromBuildDir("/path/to/build/dir").then(
nextPages => {
expect(nextPages).toEqual([]);
}
);

return getNextPagesFromBuildDir("/path/to/build/dir").then(nextPages => {
expect(nextPages).toEqual([]);
});
mockedStream.emit("end");

return getPagesPromise;
});

it("should return two next pages", () => {
expect.assertions(3);
expect.assertions(5);

fs.readdir.mockImplementationOnce((path, cb) =>
cb(null, ["index.js", "about.js"])
);
const buildDir = "./build";
const resolvedBuildDir = path.resolve(buildDir);

return getNextPagesFromBuildDir("/path/to/build").then(nextPages => {
const promise = getNextPagesFromBuildDir(buildDir).then(nextPages => {
expect(nextPages).toHaveLength(2);
expect(nextPages[0].pageName).toEqual("index");
expect(nextPages[0].pagePath).toEqual("build/index.js");
expect(nextPages[1].pageName).toEqual("about");
expect(nextPages[1].pagePath).toEqual("build/about.js");
});

mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "index.js")
});
mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "about.js")
});
mockedStream.emit("end");

return promise;
});

it("should pass provided pageConfig to next pages", () => {
expect.assertions(2);

fs.readdir.mockImplementationOnce((path, cb) =>
cb(null, ["index.js", "about.js"])
);

const indexPageConfigOverride = { foo: "bar" };
const aboutPageConfigOverride = { bar: "baz" };

Expand All @@ -49,7 +73,7 @@ describe("getNextPagesFromBuildDir", () => {
about: aboutPageConfigOverride
};

return getNextPagesFromBuildDir("/path/to/build", pageConfig).then(
const promise = getNextPagesFromBuildDir("/path/to/build", pageConfig).then(
nextPages => {
expect(nextPages[0].serverlessFunctionOverrides).toEqual(
indexPageConfigOverride
Expand All @@ -59,43 +83,111 @@ describe("getNextPagesFromBuildDir", () => {
);
}
);

mockedStream.emit("data", { path: `/path/to/build/index.js` });
mockedStream.emit("data", { path: `/path/to/build/about.js` });
mockedStream.emit("end");

return promise;
});

it("should log pages found", () => {
expect.assertions(1);

const buildDir = "/path/to/build";

fs.readdir.mockImplementationOnce((path, cb) => cb(null, ["admin.js"]));

return getNextPagesFromBuildDir(buildDir).then(() => {
const promise = getNextPagesFromBuildDir("/path/to/build").then(() => {
expect(logger.log).toBeCalledWith(`Found 1 next page(s)`);
});

mockedStream.emit("data", { path: `/path/to/build/about.js` });
mockedStream.emit("end");

return promise;
});

it("should skip _app and _document pages", () => {
expect.assertions(2);

fs.readdir.mockImplementationOnce((path, cb) =>
cb(null, ["_app.js", "_document.js", "_error.js"])
);
const buildDir = "./build";
const resolvedBuildDir = path.resolve(buildDir);

return getNextPagesFromBuildDir("/path/to/build").then(nextPages => {
const promise = getNextPagesFromBuildDir(buildDir).then(nextPages => {
expect(nextPages).toHaveLength(1);
expect(nextPages[0].pageName).toEqual("_error");
});

mockedStream.emit("data", { path: path.join(resolvedBuildDir, "_app.js") });
mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "_document.js")
});
mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "_error.js")
});
mockedStream.emit("end");

return promise;
});

it("should skip compatLayer file", () => {
expect.assertions(2);

fs.readdir.mockImplementationOnce((path, cb) =>
cb(null, ["compatLayer.js", "home.js"])
const promise = getNextPagesFromBuildDir("/path/to/build").then(
nextPages => {
expect(nextPages).toHaveLength(1);
expect(nextPages[0].pageName).toEqual("home");
}
);

return getNextPagesFromBuildDir("/path/to/build").then(nextPages => {
expect(nextPages).toHaveLength(1);
expect(nextPages[0].pageName).toEqual("home");
mockedStream.emit("data", { path: `/path/to/build/compatLayer.js` });
mockedStream.emit("data", { path: `/path/to/build/home.js` });
mockedStream.emit("end");

return promise;
});

it("should handle nested pages", () => {
expect.assertions(5);

const buildDir = "./build";
const resolvedBuildDir = path.resolve(buildDir);

const promise = getNextPagesFromBuildDir(buildDir).then(nextPages => {
expect(nextPages).toHaveLength(2);
expect(nextPages[0].pageName).toEqual("hello-world");
expect(nextPages[0].pagePath).toEqual("build/one/hello-world.js");
expect(nextPages[1].pageName).toEqual("hello-world");
expect(nextPages[1].pagePath).toEqual("build/one/two/hello-world.js");
});

mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "one/hello-world.js")
});
mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "one/two/hello-world.js")
});
mockedStream.emit("end");

return promise;
});

it("should skip page directories", () => {
expect.assertions(1);

const buildDir = "./build";
const resolvedBuildDir = path.resolve(buildDir);
fs.lstatSync.mockReturnValue({ isDirectory: () => true });

const promise = getNextPagesFromBuildDir(buildDir).then(nextPages => {
expect(nextPages).toHaveLength(0);
});

mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "one/")
});
mockedStream.emit("data", {
path: path.join(resolvedBuildDir, "one/two/")
});
mockedStream.emit("end");

return promise;
});
});
41 changes: 31 additions & 10 deletions lib/getNextPagesFromBuildDir.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,48 @@
const fs = require("fs");
const path = require("path");
const { promisify } = require("util");
const walkDir = require("klaw");
const fs = require("fs");
const logger = require("../utils/logger");
const NextPage = require("../classes/NextPage");

const readdirAsync = promisify(fs.readdir);

const logPages = nextPages => {
const pageNames = nextPages.map(p => p.pageName);
logger.log(`Found ${pageNames.length} next page(s)`);
};

const excludeBuildFiles = ["_app.js", "_document.js", "compatLayer.js"];

const getBuildFiles = buildDir => {
const buildFiles = [];
return new Promise(resolve => {
const stream = walkDir(buildDir);
stream
.on("data", item => {
const isFile = !fs.lstatSync(item.path).isDirectory();

if (isFile) {
buildFiles.push(item.path);
}
})
.on("end", () => {
resolve(buildFiles);
});
});
};

module.exports = async (buildDir, pageConfig = {}) => {
const buildFiles = await readdirAsync(buildDir);
const buildFiles = await getBuildFiles(buildDir);

const nextPages = buildFiles
.filter(bf => !excludeBuildFiles.includes(bf))
.map(fileName => {
const pagePath = path.join(buildDir, fileName);
const resolvedBuildDir = path.resolve(buildDir);
const buildDirParentDir = path.join(resolvedBuildDir, "..");

const nextPage = new NextPage(pagePath);
const nextPages = buildFiles
.map(function normaliseFilePath(fullFilePath) {
const normalisedFilePath = path.relative(buildDirParentDir, fullFilePath);
return normalisedFilePath;
})
.filter(bf => !excludeBuildFiles.includes(path.basename(bf)))
.map(normalisedFilePath => {
const nextPage = new NextPage(normalisedFilePath);
nextPage.serverlessFunctionOverrides = pageConfig[nextPage.pageName];

return nextPage;
Expand Down

0 comments on commit 394a7fc

Please sign in to comment.