@@ -6,19 +6,39 @@ import { tmpdir } from "node:os";
6
6
import { join } from "node:path" ;
7
7
import { URL } from "node:url" ;
8
8
import { watch } from "chokidar" ;
9
+ import { render } from "ink" ;
10
+ import Table from "ink-table" ;
9
11
import { getType } from "mime" ;
12
+ import React from "react" ;
13
+ import { format } from "timeago.js" ;
10
14
import { buildWorker } from "../pages/functions/buildWorker" ;
11
15
import { generateConfigFromFileTree } from "../pages/functions/filepath-routing" ;
12
16
import { writeRoutesModule } from "../pages/functions/routes" ;
17
+ import { fetchResult } from "./cfetch" ;
18
+ import { readConfig } from "./config" ;
13
19
import { FatalError } from "./errors" ;
14
20
import openInBrowser from "./open-in-browser" ;
15
21
import { toUrlPath } from "./paths" ;
22
+ import { requireAuth } from "./user" ;
16
23
import type { Config } from "../pages/functions/routes" ;
17
24
import type { Headers , Request , fetch } from "@miniflare/core" ;
18
25
import type { BuildResult } from "esbuild" ;
19
26
import type { MiniflareOptions } from "miniflare" ;
20
27
import type { BuilderCallback } from "yargs" ;
21
28
29
+ type ConfigPath = string | undefined ;
30
+
31
+ export type Project = {
32
+ name : string ;
33
+ domains : Array < string > ;
34
+ source ?: {
35
+ type : string ;
36
+ } ;
37
+ latest_deployment : {
38
+ modified_on : string ;
39
+ } ;
40
+ } ;
41
+
22
42
// Defer importing miniflare until we really need it. This takes ~0.5s
23
43
// and also modifies some `stream/web` and `undici` prototypes, so we
24
44
// don't want to do this if pages commands aren't being called.
@@ -1059,6 +1079,29 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1059
1079
} ) ;
1060
1080
}
1061
1081
)
1082
+ )
1083
+ . command ( "project" , false , ( yargs ) =>
1084
+ yargs . command (
1085
+ "list" ,
1086
+ "List your Cloudflare Pages projects" ,
1087
+ ( ) => { } ,
1088
+ async ( args ) => {
1089
+ const config = readConfig ( args . config as ConfigPath , args ) ;
1090
+ const accountId = await requireAuth ( config ) ;
1091
+
1092
+ const projects : Array < Project > = await listProjects ( { accountId } ) ;
1093
+
1094
+ const data = projects . map ( ( project ) => {
1095
+ return {
1096
+ "Project Name" : project . name ,
1097
+ "Project Domains" : `${ project . domains . join ( ", " ) } ` ,
1098
+ "Git Provider" : project . source ? "Yes" : "No" ,
1099
+ "Last Modified" : format ( project . latest_deployment . modified_on ) ,
1100
+ } ;
1101
+ } ) ;
1102
+ render ( < Table data = { data } > </ Table > ) ;
1103
+ }
1104
+ )
1062
1105
) ;
1063
1106
} ;
1064
1107
@@ -1067,3 +1110,29 @@ const invalidAssetsFetch: typeof fetch = () => {
1067
1110
"Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
1068
1111
) ;
1069
1112
} ;
1113
+
1114
+ const listProjects = async ( {
1115
+ accountId,
1116
+ } : {
1117
+ accountId : string ;
1118
+ } ) : Promise < Array < Project > > => {
1119
+ const pageSize = 10 ;
1120
+ let page = 1 ;
1121
+ const results = [ ] ;
1122
+ while ( results . length % pageSize === 0 ) {
1123
+ const json : Array < Project > = await fetchResult (
1124
+ `/accounts/${ accountId } /pages/projects` ,
1125
+ { } ,
1126
+ new URLSearchParams ( {
1127
+ per_page : pageSize . toString ( ) ,
1128
+ page : page . toString ( ) ,
1129
+ } )
1130
+ ) ;
1131
+ page ++ ;
1132
+ results . push ( ...json ) ;
1133
+ if ( json . length < pageSize ) {
1134
+ break ;
1135
+ }
1136
+ }
1137
+ return results ;
1138
+ } ;
0 commit comments