Skip to content

Commit

Permalink
feat: Creating yarn PnP projects with create-remix
Browse files Browse the repository at this point in the history
- Adds a new option to the create-remix command for selecting a package manager
    - choose between npm, yarn (without PnP) and yarn (with PnP)
  • Loading branch information
cmd-johnson committed Feb 22, 2022
1 parent 5c57e86 commit f461671
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 10 deletions.
35 changes: 29 additions & 6 deletions packages/create-remix/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import chalkAnimation from "chalk-animation";
import inquirer from "inquirer";
import meow from "meow";

import type { Lang, Server, Stack } from ".";
import { appType } from ".";
import { createApp } from ".";
import type { Lang, PackageManager, Server, Stack } from ".";
import { appType, createApp, getInstallCommand } from ".";

const help = `
Usage:
Expand Down Expand Up @@ -69,12 +68,14 @@ async function run() {
stack?: never;
server: Server;
lang: Lang;
packageManager: PackageManager;
install: boolean;
}
| {
appType: "stack";
stack: Stack;
server?: never;
packageManager?: never;
install: boolean;
}
>([
Expand Down Expand Up @@ -142,10 +143,31 @@ async function run() {
{ name: "JavaScript", value: "js" }
]
},
{
name: "packageManager",
type: "list",
message: "Which package manager?",
when(answers) {
return answers.appType !== appType.stack;
},
loop: false,
choices: [
{ name: "npm", value: "npm" },
{ name: "yarn (without PnP)", value: "yarn" },
{ name: "yarn (with PnP enabled)", value: "yarn-pnp" }
]
},
{
name: "install",
type: "confirm",
message: "Do you want me to run `npm install`?",
message: answers => {
const packageManager = typeof answers.packageManager === 'string'
? answers.packageManager
: 'npm';

const command = getInstallCommand(packageManager);
return `Do you want me to run \`${command}\`?`;
},
default: true
}
]);
Expand All @@ -155,14 +177,15 @@ async function run() {
projectDir,
lang: "ts",
stack: answers.stack,
install: answers.install
install: answers.install,
});
} else {
await createApp({
projectDir,
lang: answers.lang,
server: answers.server,
install: answers.install
install: answers.install,
packageManager: answers.packageManager,
});
}
}
53 changes: 49 additions & 4 deletions packages/create-remix/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export type AppType = typeof appType[keyof typeof appType];

export type Lang = "ts" | "js";

export type PackageManager =
| "npm"
| "yarn"
| "yarn-pnp";

export type CreateAppArgs =
| {
projectDir: string;
Expand All @@ -35,6 +40,7 @@ export type CreateAppArgs =
stack?: never;
install: boolean;
quiet?: boolean;
packageManager?: PackageManager;
}
| {
projectDir: string;
Expand All @@ -43,13 +49,15 @@ export type CreateAppArgs =
stack: Stack;
install: boolean;
quiet?: boolean;
packageManager?: PackageManager;
};

async function createApp({
export async function createApp({
projectDir,
lang,
install,
quiet,
packageManager = 'npm',
...rest
}: CreateAppArgs) {
let server = rest.stack ? rest.stack : rest.server;
Expand Down Expand Up @@ -95,6 +103,16 @@ async function createApp({
await fse.copy(serverLangTemplate, projectDir, { overwrite: true });
}

// copy the package manager template
const packageManagerLangTemplate = path.resolve(
__dirname,
"templates",
`${packageManager}_${lang}`
);
if (fse.existsSync(packageManagerLangTemplate)) {
await fse.copy(packageManagerLangTemplate, projectDir, { overwrite: true });
}

// rename dotfiles
let dotfiles = ["gitignore", "github", "dockerignore", "env.example"];
await Promise.all(
Expand All @@ -114,8 +132,13 @@ async function createApp({
appPkg.dependencies = appPkg.dependencies || {};
appPkg.devDependencies = appPkg.devDependencies || {};
let serverPkg = require(path.join(serverTemplate, "package.json"));
let packageManagerPkgPath = path.join(packageManagerLangTemplate, "package.json");
let packageManagerPkg = fse.existsSync(packageManagerPkgPath)
? require(packageManagerPkgPath)
: {};

["dependencies", "devDependencies", "scripts"].forEach(key => {
Object.assign(appPkg[key], serverPkg[key]);
Object.assign(appPkg[key], serverPkg[key], packageManagerPkg[key]);
});

appPkg.main = serverPkg.main;
Expand All @@ -133,16 +156,30 @@ async function createApp({
}
});


appPkg = sortPackageJSON(appPkg);

if (packageManager === "yarn-pnp") {
// yarn PnP does not support the remix package's magic, so we remove it from
// the dependencies and delete the postinstall hook
delete appPkg.dependencies.remix;
delete appPkg.scripts.postinstall;
}

// write package.json
await fse.writeFile(
path.join(projectDir, "package.json"),
JSON.stringify(appPkg, null, 2)
);

// enable yarn PnP if requested
if (packageManager === "yarn-pnp") {
execSync("yarn set version berry", { stdio: "inherit", cwd: projectDir });
}

if (install) {
execSync("npm install", { stdio: "inherit", cwd: projectDir });
const command = getInstallCommand(packageManager);
execSync(command, { stdio: "inherit", cwd: projectDir });
}

let serverScript = path.resolve(serverTemplate, "scripts/init.js");
Expand Down Expand Up @@ -174,4 +211,12 @@ async function createApp({
}
}

export { createApp };
export function getInstallCommand(packageManager: PackageManager): string {
switch (packageManager) {
case "npm":
return "npm install";
case "yarn":
case "yarn-pnp":
return "yarn install";
}
}
14 changes: 14 additions & 0 deletions packages/create-remix/templates/yarn-pnp/gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules

/.cache
/server/build
/public/build
.env

.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { hydrate } from "react-dom";
import { RemixBrowser } from "@remix-run/react";

hydrate(<RemixBrowser />, document);
20 changes: 20 additions & 0 deletions packages/create-remix/templates/yarn-pnp_js/app/entry.server.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { renderToString } from "react-dom/server";
import { RemixServer } from "@remix-run/react";

export default function handleRequest(
request,
responseStatusCode,
responseHeaders,
remixContext
) {
const markup = renderToString(
<RemixServer context={remixContext} url={request.url} />
);

responseHeaders.set("Content-Type", "text/html");

return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders
});
}
31 changes: 31 additions & 0 deletions packages/create-remix/templates/yarn-pnp_js/app/root.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration
} from "@remix-run/react";

export function meta() {
return { title: "New Remix App" };
}

export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
10 changes: 10 additions & 0 deletions packages/create-remix/templates/yarn-pnp_js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"dependencies": {
"@remix-run/serve": "*",
"@remix-run/server-runtime": "*"
},
"devDependencies": {
"esbuild": ">=0.10.0",
"@yarnpkg/esbuild-plugin-pnp": "^2.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { hydrate } from "react-dom";
import { RemixBrowser } from "@remix-run/react";

hydrate(<RemixBrowser />, document);
21 changes: 21 additions & 0 deletions packages/create-remix/templates/yarn-pnp_ts/app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { renderToString } from "react-dom/server";
import { RemixServer } from "@remix-run/react";
import type { EntryContext } from "@remix-run/server-runtime";

export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
const markup = renderToString(
<RemixServer context={remixContext} url={request.url} />
);

responseHeaders.set("Content-Type", "text/html");

return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders
});
}
32 changes: 32 additions & 0 deletions packages/create-remix/templates/yarn-pnp_ts/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration
} from "@remix-run/react";
import type { MetaFunction } from "@remix-run/server-runtime";

export const meta: MetaFunction = () => {
return { title: "New Remix App" };
};

export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
11 changes: 11 additions & 0 deletions packages/create-remix/templates/yarn-pnp_ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"dependencies": {
"@remix-run/serve": "*",
"@remix-run/server-runtime": "*"
},
"devDependencies": {
"esbuild": ">=0.10.0",
"@types/node": "^17.0.8",
"@yarnpkg/esbuild-plugin-pnp": "^2.0.0"
}
}

0 comments on commit f461671

Please sign in to comment.