Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(remix-dev): add option to remove typescript types during project creation #2317

Merged
merged 28 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eda5a74
chore: use name as given
mcansh Mar 21, 2022
82632de
test: add test for converting a project to javascript
mcansh Mar 15, 2022
228dc0c
chore: default useTypeScript to true
mcansh Mar 15, 2022
9f292a2
update extension used in test
mcansh Mar 15, 2022
d234472
test: add globalSetup to remix-dev
mcansh Mar 15, 2022
0edbe5e
chore: import babel preset and plugin
mcansh Mar 15, 2022
c62ae0a
chore: review changes
mcansh Mar 16, 2022
93aad02
test: add subfolder to tmp for each type of test
mcansh Mar 16, 2022
49fb048
test: re-add test for file URL to a tarball on disk, and separate str…
mcansh Mar 16, 2022
022d86a
test: update CLI output
mcansh Mar 16, 2022
8900894
fix: exclude typescript dep
mcansh Mar 16, 2022
481cbb1
test: add template options for createFixture
mcansh Mar 21, 2022
d33188c
fix: pass projectDir to babel
mcansh Mar 21, 2022
8d96140
chore: remove javascript templates
mcansh Mar 21, 2022
45a98da
feat: convert template to js
mcansh Mar 21, 2022
1906eea
test: update afterAll hook
mcansh Mar 21, 2022
9d72055
chore: update template names
mcansh Mar 21, 2022
7be390a
test: update opt out dep message
mcansh Mar 22, 2022
4c84d25
test: update template URL until this PR is merged
mcansh Mar 22, 2022
44a10ef
test: update js conversion test
mcansh Mar 22, 2022
db12a71
test: updated the url so i didnt have to skip it lol
mcansh Mar 22, 2022
704d8ad
test: update beforeAll
mcansh Mar 22, 2022
2145618
chore: code review
mcansh Mar 22, 2022
5b1e52a
test: skip test instead of adding a todo
mcansh Mar 22, 2022
2cbbd26
chore: emptyDir will create the dir if it doesnt exist
mcansh Mar 22, 2022
2c6225c
Merge branch 'dev' into logan/ts-strip-types
mcansh Mar 22, 2022
dc179ea
chore: code review
mcansh Mar 22, 2022
e4a89e0
Merge branch 'dev' into logan/ts-strip-types
mcansh Mar 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion integration/helpers/create-fixture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ const REMIX_SOURCE_BUILD_DIR = path.join(process.cwd(), "build");

interface FixtureInit {
files: { [filename: string]: string };
template?: string;
template?:
| "arc"
| "cloudflare-pages"
| "cloudflare-workers"
| "deno"
| "express"
| "fly"
| "netlify"
| "remix"
| "vercel";
}

export type Fixture = Awaited<ReturnType<typeof createFixture>>;
Expand Down
4 changes: 2 additions & 2 deletions integration/helpers/global-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from "fs/promises";
import path from "path";
import setupPuppeteer from "jest-environment-puppeteer/setup";

export const TMP_DIR = path.join(process.cwd(), ".tmp");
export const TMP_DIR = path.join(process.cwd(), ".tmp", "integration");

// TODO: get rid of React Router `console.warn` when no routes match when testing
console.warn = () => {};
Expand All @@ -13,5 +13,5 @@ export default async function setup(globalConfig: any) {
force: true,
recursive: true,
});
await fs.mkdir(TMP_DIR);
await fs.mkdir(TMP_DIR, { recursive: true });
mcansh marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
setupFilesAfterEnv: [
"<rootDir>/packages/remix-dev/__tests__/setupAfterEnv.ts",
],
globalSetup: process.env.CI ? undefined : "<rootDir>/jest/buildRemix.ts",
},
{
displayName: "remix-express",
Expand Down
31 changes: 27 additions & 4 deletions packages/remix-dev/__tests__/cli-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ const remix = path.resolve(
"../../../build/node_modules/@remix-run/dev/cli.js"
);

const TEMP_DIR = path.join(process.cwd(), ".tmp", "create-remix");

describe("remix cli", () => {
beforeAll(() => {
beforeAll(async () => {
if (!fs.existsSync(remix)) {
throw new Error(`Cannot run Remix CLI tests w/out building Remix`);
}

await fsp.rm(TEMP_DIR, { force: true, recursive: true });
await fsp.mkdir(TEMP_DIR, { recursive: true });
});

describe("the --help flag", () => {
Expand Down Expand Up @@ -161,8 +166,7 @@ describe("remix cli", () => {

function getProjectDir(name: string) {
return path.join(
process.cwd(),
".tmp",
TEMP_DIR,
`${name}-${Math.random().toString(32).slice(2)}`
);
}
Expand Down Expand Up @@ -251,7 +255,7 @@ describe("remix cli", () => {
`💿 That's it! \`cd\` into "${projectDir}" and check the README for development and deploy instructions!`
);
expect(fs.existsSync(path.join(projectDir, "package.json"))).toBeTruthy();
expect(fs.existsSync(path.join(projectDir, "app/root.jsx"))).toBeTruthy();
expect(fs.existsSync(path.join(projectDir, "app/root.tsx"))).toBeTruthy();
});

it("works for a path to a tarball on disk", async () => {
Expand Down Expand Up @@ -290,6 +294,25 @@ describe("remix cli", () => {
expect(fs.existsSync(path.join(projectDir, "app/root.tsx"))).toBeTruthy();
});

it("converts a template to javascript", async () => {
let projectDir = getProjectDir("template-to-js");
let { stdout } = await execFile("node", [
remix,
"create",
projectDir,
"--template",
"blues-stack",
"--no-install",
"--no-typescript",
]);
expect(stdout.trim()).toBe(
`💿 You've opted out of running the remix.init script, you can run it manually with \`npx remix init\`
💿 That's it! \`cd\` into "${projectDir}" and check the README for development and deploy instructions!`
);
expect(fs.existsSync(path.join(projectDir, "package.json"))).toBeTruthy();
expect(fs.existsSync(path.join(projectDir, "app/root.tsx"))).toBeTruthy();
});

it("works for a file path to a directory on disk", async () => {
let projectDir = getProjectDir("local-directory");
let { stdout } = await execFile("node", [
Expand Down
104 changes: 91 additions & 13 deletions packages/remix-dev/cli/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import * as semver from "semver";
import { fileURLToPath } from "url";
import { execSync } from "child_process";
import sortPackageJSON from "sort-package-json";
import glob from "fast-glob";
import * as babel from "@babel/core";
// @ts-expect-error these modules dont have types
import babelPluginSyntaxJSX from "@babel/plugin-syntax-jsx";
// @ts-expect-error these modules dont have types
import babelPresetTypeScript from "@babel/preset-typescript";
import prettier from "prettier";

import packageJson from "../package.json";

Expand All @@ -28,7 +35,7 @@ export async function createApp({
projectDir,
remixVersion = remixDevPackageVersion,
installDeps,
useTypeScript,
useTypeScript = true,
githubToken = process.env.GITHUB_TOKEN,
}: CreateAppArgs) {
// Check the node version
Expand Down Expand Up @@ -135,15 +142,10 @@ export async function createApp({
appPkg = sortPackageJSON(appPkg);
await fse.writeJSON(path.join(projectDir, "package.json"), appPkg, {
spaces: 2,
replacer: null,
});

if (!useTypeScript) {
// TODO:
// 1. Convert all .ts files in the template to .js
// 2. Rename the tsconfig.json to jsconfig.json
// 3. Remove @types/* and typescript from package.json
// 4. Remove typecheck npm script from package.json
await deTypeScriptify(projectDir);
}

if (installDeps) {
Expand Down Expand Up @@ -198,9 +200,7 @@ async function downloadAndExtractTemplateOrExample(

let cwd = path.dirname(projectDir);
let desiredDir = path.basename(projectDir);
let exampleOrTemplateName =
type === "templates" && options.useTypeScript ? `${name}-ts` : name;
let templateDir = path.join(desiredDir, type, exampleOrTemplateName);
let templateDir = path.join(desiredDir, type, name);
await pipeline(
response.body.pipe(gunzip()),
tar.extract(cwd, {
Expand Down Expand Up @@ -391,9 +391,8 @@ async function isRemixTemplate(
);
}
let results = await promise.json();
let possibleTemplateName = useTypeScript ? `${name}-ts` : name;
let template = results.find((result: any) => {
return result.name === possibleTemplateName;
return result.name === name;
});
if (!template) return undefined;
return template.name;
Expand All @@ -415,7 +414,9 @@ async function isRemixExample(name: string, token?: string) {
);
}
let results = await promise.json();
let example = results.find((result: any) => result.name === name);
let example = results.find((result: any) => {
return result.name === name;
});
if (!example) return undefined;
return example.name;
}
Expand Down Expand Up @@ -451,3 +452,80 @@ async function detectTemplateType(
}
return "remoteTarball";
}

function untype(filename: string, source: string, projectDir: string): string {
mcansh marked this conversation as resolved.
Show resolved Hide resolved
let result = babel.transformSync(source, {
filename,
presets: [[babelPresetTypeScript, { jsx: "preserve" }]],
plugins: [babelPluginSyntaxJSX],
compact: false,
retainLines: true,
cwd: projectDir,
});

if (!result || !result.code) {
throw new Error("Could not parse typescript");
}

/*
Babel's `compact` and `retainLines` options are both bad at formatting code.
Use Prettier for nicer formatting.
*/
return prettier.format(result.code, { parser: "babel-ts" });
mcansh marked this conversation as resolved.
Show resolved Hide resolved
}

async function deTypeScriptify(projectDir: string) {
// 1. Convert all .ts files in the template to .js
let entries = glob.sync("**/*.+(ts|tsx)", {
cwd: projectDir,
absolute: true,
});
for (let entry of entries) {
if (entry.endsWith(".d.ts")) {
fse.removeSync(entry);
continue;
}

let contents = fse.readFileSync(entry, "utf8");
let filename = path.basename(entry);
let untyped = untype(filename, contents, projectDir);

fse.writeFileSync(entry, untyped, "utf8");
if (entry.endsWith(".tsx")) {
fse.renameSync(entry, entry.replace(/\.tsx?$/, ".jsx"));
} else {
fse.renameSync(entry, entry.replace(/\.ts?$/, ".js"));
}
}

// 2. Rename the tsconfig.json to jsconfig.json
if (fse.existsSync(path.join(projectDir, "tsconfig.json"))) {
fse.renameSync(
path.join(projectDir, "tsconfig.json"),
path.join(projectDir, "jsconfig.json")
);
}

// 3. Remove @types/* and typescript from package.json
let packageJson = path.join(projectDir, "package.json");
if (!fse.existsSync(packageJson)) {
throw new Error("Could not find package.json");
}
let pkg = JSON.parse(fse.readFileSync(packageJson, "utf8"));
let devDeps = pkg.devDependencies || {};
let newPackageJson = {
...pkg,
devDependencies: Object.fromEntries(
Object.entries(devDeps).filter(([name]) => {
return !name.startsWith("@types/") && name !== "typescript";
})
),
};
// 4. Remove typecheck npm script from package.json
if (pkg.scripts && pkg.scripts.typecheck) {
delete pkg.scripts.typecheck;
}
fse.writeJSONSync(path.join(projectDir, "package.json"), newPackageJson, {
spaces: 2,
});
}
3 changes: 0 additions & 3 deletions templates/arc-ts/.eslintrc

This file was deleted.

9 changes: 0 additions & 9 deletions templates/arc-ts/.gitignore

This file was deleted.

58 changes: 0 additions & 58 deletions templates/arc-ts/README.md

This file was deleted.

14 changes: 0 additions & 14 deletions templates/arc-ts/app.arc

This file was deleted.

35 changes: 0 additions & 35 deletions templates/arc-ts/package.json

This file was deleted.

12 changes: 0 additions & 12 deletions templates/arc-ts/remix.config.js

This file was deleted.

Loading