Skip to content

Commit

Permalink
feat[pages]: Add new internal pages functions optimize-routes comma…
Browse files Browse the repository at this point in the history
…nd (#1648)

This PR adds a new pages functions command called `optimize-routes`. This is an
internal-use ONLY command at the moment, and is therefore not exposed to external
consumers. The command is meant to parse a given  `_routes.json` file, optimize
and consolidate the route paths defined in it, and output the resulting optimized
file.

Co-authored-by: Carmen Popoviciu <cpopoviciu@cloudflare.com>
  • Loading branch information
CarmenPopoviciu and CarmenPopoviciu authored Aug 11, 2022
1 parent c40fca4 commit af669a1
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 58 deletions.
5 changes: 5 additions & 0 deletions .changeset/gorgeous-beds-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": patch
---

Implement new wrangler pages functions optimize-routes command
14 changes: 14 additions & 0 deletions packages/wrangler/src/__tests__/pages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ describe("pages", () => {
expect(std.out).toMatchInlineSnapshot(`
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
`);
});

it("should display for pages:functions:optimize-routes", async () => {
await expect(
runWrangler(
'pages functions optimize-routes --routes-path="/build/_routes.json" --output-routes-path="/build/_optimized-routes.json"'
)
).rejects.toThrowError();

expect(std.out).toMatchInlineSnapshot(`
"🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
`);
});
Expand Down
96 changes: 96 additions & 0 deletions packages/wrangler/src/pages/functions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import { FatalError } from "../errors";
import { logger } from "../logger";
import { isInPagesCI, ROUTES_SPEC_VERSION } from "./constants";
import {
isRoutesJSONSpec,
optimizeRoutesJSONSpec,
} from "./functions/routes-transformation";
import { pagesBetaWarning } from "./utils";
import type { YargsOptionsToInterface } from "./types";
import type { Argv } from "yargs";

type OptimizeRoutesArgs = YargsOptionsToInterface<typeof OptimizeRoutesOptions>;

export function OptimizeRoutesOptions(yargs: Argv) {
return yargs
.options({
"routes-path": {
type: "string",
demandOption: true,
description: "The location of the _routes.json file",
},
})
.options({
"output-routes-path": {
type: "string",
demandOption: true,
description: "The location of the optimized output routes file",
},
});
}

export async function OptimizeRoutesHandler({
routesPath,
outputRoutesPath,
}: OptimizeRoutesArgs) {
if (!isInPagesCI) {
// Beta message for `wrangler pages <commands>` usage
logger.log(pagesBetaWarning);
}

let routesFileContents: string;
const routesOutputDirectory = path.dirname(outputRoutesPath);

if (!existsSync(routesPath)) {
throw new FatalError(
`Oops! File ${routesPath} does not exist. Please make sure --routes-path is a valid file path (for example "/public/_routes.json").`,
1
);
}

if (
!existsSync(routesOutputDirectory) ||
!lstatSync(routesOutputDirectory).isDirectory()
) {
throw new FatalError(
`Oops! Folder ${routesOutputDirectory} does not exist. Please make sure --output-routes-path is a valid file path (for example "/public/_routes.json").`,
1
);
}

try {
routesFileContents = readFileSync(routesPath, "utf-8");
} catch (err) {
throw new FatalError(`Error while reading ${routesPath} file: ${err}`);
}

const routes = JSON.parse(routesFileContents);

if (!isRoutesJSONSpec(routes)) {
throw new FatalError(
`
Invalid _routes.json file found at: ${routesPath}. Please make sure the JSON object has the following format:
{
version: ${ROUTES_SPEC_VERSION};
include: string[];
exclude: string[];
}
`,
1
);
}

const optimizedRoutes = optimizeRoutesJSONSpec(routes);
const optimizedRoutesContents = JSON.stringify(optimizedRoutes);

try {
writeFileSync(outputRoutesPath, optimizedRoutesContents);
} catch (err) {
throw new FatalError(
`Error writing to ${outputRoutesPath} file: ${err}`,
1
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ interface RoutesJSONSpec {

type RoutesJSONRouteInput = Pick<RouteConfig, "routePath" | "middleware">[];

/**
* TODO can we do better naming?
*/
export function convertRoutesToGlobPatterns(
routes: RoutesJSONRouteInput
): string[] {
Expand Down
120 changes: 65 additions & 55 deletions packages/wrangler/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as Build from "./build";
import * as Deployments from "./deployments";
import * as Dev from "./dev";
import * as Functions from "./functions";
import * as Projects from "./projects";
import * as Publish from "./publish";
import * as Upload from "./upload";
Expand All @@ -19,66 +20,75 @@ process.on("SIGTERM", () => {
});

export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
return yargs
.command(
"dev [directory] [-- command..]",
"🧑‍💻 Develop your full-stack Pages application locally",
Dev.Options,
Dev.Handler
)
.command("functions", false, (yargs) =>
// we hide this command from help output because
// it's not meant to be used directly right now
{
return yargs.command(
"build [directory]",
"Compile a folder of Cloudflare Pages Functions into a single Worker",
Build.Options,
Build.Handler
);
}
)
.command("project", "⚡️ Interact with your Pages projects", (yargs) =>
yargs
.command(
"list",
"List your Cloudflare Pages projects",
Projects.ListOptions,
Projects.ListHandler
)
.command(
"create [project-name]",
"Create a new Cloudflare Pages project",
Projects.CreateOptions,
Projects.CreateHandler
)
.command("upload [directory]", false, Upload.Options, Upload.Handler)
.epilogue(pagesBetaWarning)
)
.command(
"deployment",
"🚀 Interact with the deployments of a project",
(yargs) =>
return (
yargs
.command(
"dev [directory] [-- command..]",
"🧑‍💻 Develop your full-stack Pages application locally",
Dev.Options,
Dev.Handler
)
/**
* `wrangler pages functions` is meant for internal use only for now,
* so let's hide this command from the help output
*/
.command("functions", false, (yargs) =>
yargs
.command(
"build [directory]",
"Compile a folder of Cloudflare Pages Functions into a single Worker",
Build.Options,
Build.Handler
)
.command(
"optimize-routes [routesPath] [outputRoutesPath]",
"Consolidate and optimize the route paths declared in _routes.json",
Functions.OptimizeRoutesOptions,
Functions.OptimizeRoutesHandler
)
)
.command("project", "⚡️ Interact with your Pages projects", (yargs) =>
yargs
.command(
"list",
"List deployments in your Cloudflare Pages project",
Deployments.ListOptions,
Deployments.ListHandler
"List your Cloudflare Pages projects",
Projects.ListOptions,
Projects.ListHandler
)
.command(
"create [directory]",
"🆙 Publish a directory of static assets as a Pages deployment",
Publish.Options,
Publish.Handler
"create [project-name]",
"Create a new Cloudflare Pages project",
Projects.CreateOptions,
Projects.CreateHandler
)
.command("upload [directory]", false, Upload.Options, Upload.Handler)
.epilogue(pagesBetaWarning)
)
.command(
"publish [directory]",
"🆙 Publish a directory of static assets as a Pages deployment",
Publish.Options,
Publish.Handler
)
.epilogue(pagesBetaWarning);
)
.command(
"deployment",
"🚀 Interact with the deployments of a project",
(yargs) =>
yargs
.command(
"list",
"List deployments in your Cloudflare Pages project",
Deployments.ListOptions,
Deployments.ListHandler
)
.command(
"create [directory]",
"🆙 Publish a directory of static assets as a Pages deployment",
Publish.Options,
Publish.Handler
)
.epilogue(pagesBetaWarning)
)
.command(
"publish [directory]",
"🆙 Publish a directory of static assets as a Pages deployment",
Publish.Options,
Publish.Handler
)
.epilogue(pagesBetaWarning)
);
};

0 comments on commit af669a1

Please sign in to comment.