Skip to content

Commit 0d3d68c

Browse files
committed
chore(create-pyth-package): web-apps generator uses app shell now
1 parent 177946f commit 0d3d68c

File tree

15 files changed

+327
-270
lines changed

15 files changed

+327
-270
lines changed

packages/create-pyth-package/src/create-pyth-package.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import prompts from "prompts";
1515
import type { PackageJson } from "type-fest";
1616

1717
import { getAvailableFolders } from "./get-available-folders.js";
18+
import { getTakenPorts } from "./get-taken-ports.js";
1819
import { Logger } from "./logger.js";
1920
import type {
2021
CreatePythAppResponses,
@@ -70,6 +71,8 @@ function getTemplatesInputFolder(packageType: PackageType) {
7071
}
7172

7273
async function createPythApp() {
74+
const takenServerPorts = getTakenPorts();
75+
7376
const responses = (await prompts([
7477
{
7578
choices: Object.values(PackageType).map((val) => ({
@@ -117,6 +120,25 @@ async function createPythApp() {
117120
type: (_, { packageType }: InProgressCreatePythAppResponses) =>
118121
packageType === PackageType.WEBAPP ? false : "select",
119122
},
123+
{
124+
message:
125+
"On which port do you want your web application server to listen?",
126+
name: "serverPort",
127+
type: (_, { packageType }: InProgressCreatePythAppResponses) =>
128+
packageType === PackageType.WEBAPP ? "number" : false,
129+
validate: (port: number | string) => {
130+
const portStr = String(port);
131+
const taken = takenServerPorts.has(Number(port));
132+
const portHasFourDigits = portStr.length >= 4;
133+
if (taken) {
134+
return `${portStr} is already taken by another application. Please choose another port.`;
135+
}
136+
if (!portHasFourDigits) {
137+
return "please specify a port that has at least 4 digits";
138+
}
139+
return true;
140+
},
141+
},
120142
{
121143
message:
122144
"Are you intending on publishing this, publicly on NPM, for users outside of our org to use?",
@@ -144,8 +166,15 @@ async function createPythApp() {
144166
},
145167
])) as CreatePythAppResponses;
146168

147-
const { confirm, description, folder, isPublic, packageName, packageType } =
148-
responses;
169+
const {
170+
confirm,
171+
description,
172+
folder,
173+
isPublic,
174+
packageName,
175+
packageType,
176+
serverPort,
177+
} = responses;
149178

150179
if (!confirm) {
151180
Logger.warn("oops, you did not confirm your choices.");
@@ -188,6 +217,7 @@ async function createPythApp() {
188217
name: packageNameWithOrg,
189218
packageNameWithoutOrg,
190219
relativeFolder: relDest,
220+
serverPort,
191221
});
192222
await fs.writeFile(fp, updatedContents, "utf8");
193223

@@ -216,6 +246,8 @@ async function createPythApp() {
216246
execSync("pnpm i", { cwd: appRootPath.toString(), stdio: "inherit" });
217247

218248
Logger.info(`Done! ${packageNameWithOrg} is ready for development`);
249+
Logger.info("please checkout your package's README for more information:");
250+
Logger.info(` ${path.join(relDest, "README.md")}`);
219251
}
220252

221253
await createPythApp();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { execSync } from "node:child_process";
2+
3+
import appRootPath from "app-root-path";
4+
5+
export type PNPMPackageInfo = {
6+
name: string;
7+
path: string;
8+
private: boolean;
9+
version: string;
10+
};
11+
12+
/**
13+
* returns basic info about all of the monorepo packages available in
14+
* the pyth crosschain repo
15+
*/
16+
export function getAllMonorepoPackages(repoRoot = appRootPath.toString()) {
17+
const allPackages = JSON.parse(
18+
execSync("pnpm list --recursive --depth -1 --json", {
19+
cwd: repoRoot,
20+
stdio: "pipe",
21+
}).toString("utf8"),
22+
) as PNPMPackageInfo[];
23+
24+
return allPackages;
25+
}

packages/create-pyth-package/src/get-available-folders.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
import { execSync } from "node:child_process";
21
import path from "node:path";
32

43
import appRootPath from "app-root-path";
54

6-
type PNPMPackageInfo = {
7-
name: string;
8-
path: string;
9-
private: boolean;
10-
version: string;
11-
};
5+
import { getAllMonorepoPackages } from "./get-all-monorepo-packages.js";
126

137
const repoRoot = appRootPath.toString();
148

@@ -18,12 +12,7 @@ const repoRoot = appRootPath.toString();
1812
* directories for those, as a way to present the folder choice to the user.
1913
*/
2014
export function getAvailableFolders() {
21-
const allPackages = JSON.parse(
22-
execSync("pnpm list --recursive --depth -1 --json", {
23-
cwd: repoRoot,
24-
stdio: "pipe",
25-
}).toString("utf8"),
26-
) as PNPMPackageInfo[];
15+
const allPackages = getAllMonorepoPackages();
2716

2817
return [
2918
...new Set(
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import path from "node:path";
2+
3+
import fs from "fs-extra";
4+
import type { PackageJson } from "type-fest";
5+
6+
import { getAllMonorepoPackages } from "./get-all-monorepo-packages.js";
7+
8+
/**
9+
* scrapes the entire monorepo to see what next.js ports
10+
* are already taken and returns a Set() containing these ports
11+
* to assist the user in choosing one that doesn't have a collision
12+
*/
13+
export function getTakenPorts() {
14+
// "start:dev": "next dev --port 3003",
15+
// "start:prod": "next start --port 3003",
16+
const allPackages = getAllMonorepoPackages();
17+
18+
const out = new Set<number>();
19+
20+
for (const info of allPackages) {
21+
const pjsonPath = path.join(info.path, "package.json");
22+
const pjson = JSON.parse(fs.readFileSync(pjsonPath, "utf8")) as PackageJson;
23+
const scripts = pjson.scripts ?? {};
24+
25+
for (const scriptVal of Object.values(scripts)) {
26+
if (!scriptVal) continue;
27+
if (!scriptVal.includes("next ")) continue;
28+
29+
const [, portViaFlagStr = ""] = /--port\s+(\d+)/.exec(scriptVal) ?? [];
30+
const [, portViaEnvStr = ""] = /^PORT=(\d+)/.exec(scriptVal) ?? [];
31+
const port = Number(portViaFlagStr || portViaEnvStr);
32+
33+
if (!Number.isNaN(port)) out.add(port);
34+
}
35+
}
36+
37+
return out;
38+
}

packages/create-pyth-package/src/templates/cli/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ build/
1111
node_modules/
1212
package.json
1313
tsconfig*.json
14+
turbo.json

packages/create-pyth-package/src/templates/library/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ build/
1111
node_modules/
1212
package.json
1313
tsconfig*.json
14+
turbo.json

packages/create-pyth-package/src/templates/web-app/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ build/
1111
node_modules/
1212
package.json
1313
tsconfig*.json
14+
turbo.json

packages/create-pyth-package/src/templates/web-app/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
"fix:format": "prettier --write .",
1818
"fix:lint:eslint": "eslint --fix .",
1919
"fix:lint:stylelint": "stylelint --fix 'src/**/*.scss'",
20-
"pull:env": "[ $CI ] || VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=TODO_FILL_ME_IN vercel env pull",
21-
"start:dev": "next dev --port 3003",
22-
"start:prod": "next start --port 3003",
20+
"pull:env": "echo 'once {{name}} has been setup in the Vercel UI, you can replace this script with the contents in the pull:env:placeholder, below, as well as the VERCEL_PROJECT_ID variable value'",
21+
"pull:env:placeholder": "[ $CI ] || VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=TODO_FILL_ME_IN vercel env pull",
22+
"start:dev": "next dev --port {{serverPort}}",
23+
"start:prod": "next start --port {{serverPort}}",
2324
"test:format": "prettier --check .",
2425
"test:lint:eslint": "eslint . --max-warnings 0",
2526
"test:lint:stylelint": "stylelint 'src/**/*.scss' --max-warnings 0",
@@ -46,10 +47,12 @@
4647
"dependencies": {
4748
"@next/third-parties": "catalog:",
4849
"@pythnetwork/component-library": "workspace:*",
50+
"@pythnetwork/react-hooks": "workspace:*",
4951
"@react-hookz/web": "catalog:",
5052
"clsx": "catalog:",
5153
"framer-motion": "catalog:",
5254
"next": "catalog:",
55+
"nuqs": "catalog:",
5356
"pino": "catalog:",
5457
"react": "catalog:",
5558
"react-aria": "catalog:",
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
:root {
22
--background: #ffffff;
33
--foreground: #171717;
4-
}
54

6-
@theme inline {
7-
--color-background: var(--background);
8-
--color-foreground: var(--foreground);
9-
--font-sans: var(--font-geist-sans);
10-
--font-mono: var(--font-geist-mono);
5+
--font-sans: "Geist Sans", Arial, Helvetica, sans-serif;
6+
--font-mono: "Geist Mono", monospace;
117
}
128

139
@media (prefers-color-scheme: dark) {
@@ -20,5 +16,5 @@
2016
body {
2117
background: var(--background);
2218
color: var(--foreground);
23-
font-family: Arial, Helvetica, sans-serif;
19+
font-family: var(--font-sans);
2420
}

packages/create-pyth-package/src/templates/web-app/src/app/layout.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { AppShell } from "@pythnetwork/component-library/AppShell";
2+
import { NuqsAdapter } from "@pythnetwork/react-hooks/nuqs-adapters-next";
13
import type { Metadata } from "next";
24
import { Geist, Geist_Mono } from "next/font/google";
3-
import "./globals.css";
5+
import "./globals.scss";
6+
import type { ReactNode } from "react";
47

58
const geistSans = Geist({
69
variable: "--font-geist-sans",
@@ -13,21 +16,27 @@ const geistMono = Geist_Mono({
1316
});
1417

1518
export const metadata: Metadata = {
16-
title: "Create Next App",
17-
description: "Generated by create next app",
19+
title: "{{packageNameWithoutOrg}}",
20+
description: "{{description}}",
1821
};
1922

2023
export default function RootLayout({
2124
children,
2225
}: Readonly<{
23-
children: React.ReactNode;
26+
children: ReactNode;
2427
}>) {
2528
return (
2629
<html lang="en">
2730
<body
2831
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
2932
>
30-
{children}
33+
<AppShell
34+
appName="{{packageNameWithoutOrg}}"
35+
enableAccessibilityReporting
36+
providers={[NuqsAdapter]}
37+
>
38+
{children}
39+
</AppShell>
3140
</body>
3241
</html>
3342
);

0 commit comments

Comments
 (0)