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(create-next-app): update install process, add support for --tailwind options #24709

Closed
wants to merge 27 commits into from

Conversation

ctjlewis
Copy link
Contributor

@ctjlewis ctjlewis commented May 2, 2021

Forked from my other PR, completes the rapid-fire development loop of Next + TypeScript + Tailwind by allowing for --typescript --tailwind. This involved refactoring the template logic significantly, but it is much more well-documented and easier to extend now, if there was ever an interest in adding future options.

Sorry for the two rapid-fire PRs (which weren't asked for), I just wanted this functionality and know it is widely requested. This was originally not such a large rewrite, but naturally getting things to work properly involved adjusting a lot of the surrounding logic. I understand this code may not get used, but wanted to contribute it back anyway. Genuinely very sorry for how much there is to review here, but I tried to keep it well-documented.

Updates to install process

Support for devDependencies

Support for devDeps during install was added in the TypeScript PR (#24655) since the use case implies it. The vast majority of the install logic was refactored and reorganized in addition to this change.

By default, react and react-dom are dependencies, and next is a devDepenedency, but a case could be added for any arbitrary option to add dependencies (or template installs, as seen below). For instance, tailwind: true will imply Tailwind and PostCSS devDeps:

  /**
   * Add dependencies based on the given options here.
   */
  for (const [option, enabled] of Object.entries(options)) {
    if (!enabled) continue

    switch (option) {
      case 'typescript':
        /**
         * TypeScript projects will have type definitions and other devDependencies.
         */
        devDependencies.push('typescript', '@types/react', '@types/next')
        break

      case 'tailwind':
        /**
         * Tailwind will require a few devDependencies as well.
         * @see https://tailwindcss.com/docs/guides/nextjs
         */
        devDependencies.push(
          'tailwindcss@latest',
          'postcss@latest',
          'autoprefixer@latest'
        )
        break

      default:
       /**
         * Silently pass on any enabled options that do not imply additional
         * dependencies.
         */
        break
    }

Support for TS templates

The separate logic for installing from an example repo and installing from templates was pulled out of create-app.ts and put into separate files to make the parent function more legible and help with refactoring. Templates are now stored in the form templates/{ts|js}/{template}, and the install process for a template (helpers/install/from-template.ts) basically has two steps (this has not changed, just is more clear and self-evident in the refactored code):

  1. An initial install from an initialTemplate, which is "default" by default, i.e. templates/ts/default or templates/js/default.
  2. Any additional template installs implied by the NextCreateOptions, i.e. { tailwind: true } will imply additional template installs from templates/{ts|js}/tailwind.

The logic for additional template installs is handled in a switch/case inside helpers/install/from-template.ts. This same pattern is also observed in the dependencies/devDependencies staging, as seen above.

  /**
   * Finally, install additional template files based on the provided `options`.
   */
  for (const [option, enabled] of Object.entries(options)) {
    if (!enabled) continue
    switch (option) {
      case 'tailwind':
        /**
         * Install the Tailwind template files for the given OutputMode.
         */
        await installTemplateDirectory({
          root,
          template: 'tailwind',
          outputMode,
        })
        break

      default:
        /**
         * Silently pass on any enabled options that do not imply additional
         * templates, like `typescript: true`.
         */
        break
    }
  }

Demo

Repo created with create-next-app --typescript --tailwind:
https://github.com/ctjlewis/next-typescript-tailwind

Deployed at:
https://next-typescript-tailwind-hwv2xe88o-ctjlewis.vercel.app/

node ~/PersonalProjects/next.js/packages/create-next-app/dist/index.js --typescript --tailwind next-typescript-tailwind

Creating a new Next.js app in /home/christian/PersonalProjects/next-typescript-tailwind.

Using yarn.

Next app options:
- typescript: yes
- tailwind: yes

Installing dependencies:
- react
- react-dom

yarn add v1.22.5
warning ../../package.json: No license field
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 4 new dependencies.
info Direct dependencies
├─ react-dom@17.0.2
└─ react@17.0.2
info All dependencies
├─ js-tokens@4.0.0
├─ react-dom@17.0.2
├─ react@17.0.2
└─ scheduler@0.20.2
Done in 3.71s.

Installing devDependencies:
- next
- typescript
- @types/react
- @types/next
- tailwindcss@latest
- postcss@latest
- autoprefixer@latest

yarn add v1.22.5
warning ../../package.json: No license field
[1/4] Resolving packages...
warning @types/next@9.0.0: This is a stub types definition. next provides its own type definitions, so you do not need this installed.
[2/4] Fetching packages...
info fsevents@2.3.2: The platform "linux" is incompatible with this module.
info "fsevents@2.3.2" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 253 new dependencies.
info Direct dependencies
├─ @types/next@9.0.0
├─ @types/react@17.0.4
├─ autoprefixer@10.2.5
├─ next@10.2.0
├─ postcss@8.2.13
├─ tailwindcss@2.1.2
└─ typescript@4.2.4
info All dependencies
├─ @babel/code-frame@7.12.11
├─ @babel/helper-validator-identifier@7.14.0
├─ @babel/highlight@7.14.0
├─ @babel/runtime@7.12.5
├─ @babel/types@7.8.3
├─ @fullhuman/postcss-purgecss@3.1.3
├─ @hapi/accept@5.0.1
├─ @hapi/boom@9.1.2
├─ @next/env@10.2.0
├─ @next/polyfill-module@10.2.0
├─ @next/react-dev-overlay@10.2.0
├─ @next/react-refresh-utils@10.2.0
├─ @nodelib/fs.scandir@2.1.4
├─ @nodelib/fs.stat@2.0.4
├─ @nodelib/fs.walk@1.2.6
├─ @opentelemetry/api@0.14.0
├─ @opentelemetry/context-base@0.14.0
├─ @types/next@9.0.0
├─ @types/node@15.0.1
├─ @types/prop-types@15.7.3
├─ @types/react@17.0.4
├─ @types/scheduler@0.16.1
├─ acorn-node@1.8.2
├─ acorn-walk@7.2.0
├─ acorn@7.4.1
├─ anser@1.4.9
├─ ansi-regex@5.0.0
├─ anymatch@3.1.2
├─ array-filter@1.0.0
├─ asn1.js@5.4.1
├─ assert@2.0.0
├─ ast-types@0.13.2
├─ at-least-node@1.0.0
├─ autoprefixer@10.2.5
├─ babel-plugin-syntax-jsx@6.18.0
├─ balanced-match@1.0.2
├─ big.js@5.2.2
├─ binary-extensions@2.2.0
├─ brace-expansion@1.1.11
├─ braces@3.0.2
├─ brorand@1.1.0
├─ browserify-aes@1.2.0
├─ browserify-cipher@1.0.1
├─ browserify-des@1.0.2
├─ browserify-rsa@4.1.0
├─ browserify-sign@4.2.1
├─ browserify-zlib@0.2.0
├─ browserslist@4.16.1
├─ buffer-xor@1.0.3
├─ buffer@5.6.0
├─ bytes@3.1.0
├─ camelcase-css@2.0.1
├─ caniuse-lite@1.0.30001220
├─ chalk@2.4.2
├─ chokidar@3.5.1
├─ classnames@2.2.6
├─ color-convert@1.9.3
├─ color-name@1.1.4
├─ color-string@1.5.5
├─ color@3.1.3
├─ commander@6.2.1
├─ commondir@1.0.1
├─ concat-map@0.0.1
├─ console-browserify@1.2.0
├─ constants-browserify@1.0.0
├─ convert-source-map@1.7.0
├─ core-util-is@1.0.2
├─ create-ecdh@4.0.4
├─ create-hmac@1.1.7
├─ crypto-browserify@3.12.0
├─ css-unit-converter@1.1.2
├─ css.escape@1.5.1
├─ cssesc@3.0.0
├─ cssnano-preset-simple@2.0.0
├─ cssnano-simple@2.0.0
├─ csstype@3.0.8
├─ data-uri-to-buffer@3.0.1
├─ debug@2.6.9
├─ defined@1.0.0
├─ depd@1.1.2
├─ des.js@1.0.1
├─ detective@5.2.0
├─ didyoumean@1.2.1
├─ diffie-hellman@5.0.3
├─ dlv@1.1.3
├─ domain-browser@4.19.0
├─ electron-to-chromium@1.3.725
├─ emojis-list@2.1.0
├─ encoding@0.1.13
├─ es-abstract@1.18.0
├─ es-to-primitive@1.2.1
├─ es6-object-assign@1.1.0
├─ escape-string-regexp@1.0.5
├─ esutils@2.0.3
├─ etag@1.8.1
├─ events@3.3.0
├─ fast-glob@3.2.5
├─ fastq@1.11.0
├─ fill-range@7.0.1
├─ find-cache-dir@3.3.1
├─ find-up@4.1.0
├─ fraction.js@4.0.13
├─ fs-extra@9.1.0
├─ fs.realpath@1.0.0
├─ get-intrinsic@1.1.1
├─ get-orientation@1.1.2
├─ glob-base@0.3.0
├─ glob-parent@5.1.2
├─ glob-to-regexp@0.4.1
├─ glob@7.1.6
├─ graceful-fs@4.2.6
├─ has-bigints@1.0.1
├─ hash.js@1.1.7
├─ he@1.2.0
├─ hmac-drbg@1.0.1
├─ html-tags@3.1.0
├─ http-errors@1.7.3
├─ https-browserify@1.0.0
├─ iconv-lite@0.4.24
├─ inflight@1.0.6
├─ inherits@2.0.4
├─ is-arguments@1.1.0
├─ is-arrayish@0.3.2
├─ is-bigint@1.0.1
├─ is-binary-path@2.1.0
├─ is-boolean-object@1.1.0
├─ is-callable@1.2.3
├─ is-core-module@2.3.0
├─ is-date-object@1.0.2
├─ is-dotfile@1.0.3
├─ is-generator-function@1.0.8
├─ is-nan@1.3.2
├─ is-negative-zero@2.0.1
├─ is-number-object@1.0.4
├─ is-number@7.0.0
├─ is-regex@1.1.2
├─ is-symbol@1.0.3
├─ isarray@1.0.0
├─ jest-worker@27.0.0-next.5
├─ json5@1.0.1
├─ jsonfile@6.1.0
├─ loader-utils@1.2.3
├─ locate-path@5.0.0
├─ lodash.sortby@4.7.0
├─ lodash.toarray@4.4.0
├─ lodash.topath@4.5.2
├─ lodash@4.17.21
├─ make-dir@3.1.0
├─ merge-stream@2.0.0
├─ merge2@1.4.1
├─ micromatch@4.0.4
├─ miller-rabin@4.0.1
├─ minimatch@3.0.4
├─ minimist@1.2.5
├─ modern-normalize@1.0.0
├─ ms@2.0.0
├─ native-url@0.3.4
├─ next@10.2.0
├─ node-emoji@1.10.0
├─ node-fetch@2.6.1
├─ node-html-parser@1.4.9
├─ node-libs-browser@2.2.1
├─ node-releases@1.1.71
├─ normalize-range@0.1.2
├─ object-hash@2.1.1
├─ object-inspect@1.10.2
├─ object-is@1.1.5
├─ object.assign@4.1.2
├─ os-browserify@0.3.0
├─ p-limit@3.1.0
├─ p-locate@4.1.0
├─ p-try@2.2.0
├─ pako@1.0.11
├─ parse-asn1@5.1.6
├─ parse-glob@3.0.4
├─ path-browserify@1.0.1
├─ path-exists@4.0.0
├─ path-is-absolute@1.0.1
├─ path-parse@1.0.6
├─ picomatch@2.2.3
├─ pkg-dir@4.2.0
├─ platform@1.3.6
├─ pnp-webpack-plugin@1.6.4
├─ postcss-functions@3.0.0
├─ postcss-js@3.0.3
├─ postcss-nested@5.0.5
├─ postcss-selector-parser@6.0.5
├─ postcss@8.2.13
├─ pretty-hrtime@1.0.3
├─ process-nextick-args@2.0.1
├─ process@0.11.10
├─ prop-types@15.7.2
├─ public-encrypt@4.0.3
├─ punycode@1.4.1
├─ purgecss@3.1.3
├─ querystring-es3@0.2.1
├─ querystring@0.2.1
├─ queue-microtask@1.2.3
├─ quick-lru@5.1.1
├─ randomfill@1.0.4
├─ raw-body@2.4.1
├─ react-is@16.13.1
├─ react-refresh@0.8.3
├─ readdirp@3.5.0
├─ reduce-css-calc@2.1.8
├─ regenerator-runtime@0.13.7
├─ resolve@1.20.0
├─ reusify@1.0.4
├─ run-parallel@1.2.0
├─ safer-buffer@2.1.2
├─ semver@6.3.0
├─ setimmediate@1.0.5
├─ setprototypeof@1.1.1
├─ shell-quote@1.7.2
├─ simple-swizzle@0.2.2
├─ stacktrace-parser@0.1.10
├─ statuses@1.5.0
├─ stream-browserify@3.0.0
├─ stream-http@3.1.1
├─ stream-parser@0.3.1
├─ string_decoder@1.3.0
├─ string-hash@1.1.3
├─ string.prototype.trimend@1.0.4
├─ string.prototype.trimstart@1.0.4
├─ strip-ansi@6.0.0
├─ styled-jsx@3.3.2
├─ stylis-rule-sheet@0.0.10
├─ stylis@3.5.4
├─ supports-color@5.5.0
├─ tailwindcss@2.1.2
├─ timers-browserify@2.0.12
├─ to-arraybuffer@1.0.1
├─ to-fast-properties@2.0.0
├─ to-regex-range@5.0.1
├─ toidentifier@1.0.0
├─ tr46@1.0.1
├─ ts-pnp@1.2.0
├─ tty-browserify@0.0.1
├─ type-fest@0.7.1
├─ typescript@4.2.4
├─ unbox-primitive@1.0.1
├─ unpipe@1.0.0
├─ url@0.11.0
├─ use-subscription@1.5.1
├─ util-deprecate@1.0.2
├─ util@0.12.3
├─ vm-browserify@1.1.2
├─ watchpack@2.1.1
├─ webidl-conversions@4.0.2
├─ whatwg-url@7.1.0
├─ which-boxed-primitive@1.0.2
├─ which-typed-array@1.1.4
└─ yocto-queue@0.1.0
Done in 47.41s.

Initialized a git repository.

Success! Created next-typescript-tailwind at /home/christian/PersonalProjects/next-typescript-tailwind
Inside that directory, you can run several commands:

  yarn dev
    Starts the development server.

  yarn build
    Builds the app for production.

  yarn start
    Runs the built app in production mode.

We suggest that you begin by typing:

  cd next-typescript-tailwind
  yarn dev

Footnotes

The diff for helpers/install.ts is weird because it was moved to install.ts inside an install/ directory, i.e. at helpers/install/index.ts. It was also heavily refactored (iteratively) and is, IMO, much more manageable now as well.

@ijjk ijjk added the create-next-app Related to our CLI tool for quickly starting a new Next.js application. label May 2, 2021
@ctjlewis
Copy link
Contributor Author

ctjlewis commented May 2, 2021

Also, for the new Tailwind templates, most of the styles were redone with Tailwind classes, and a 100vh height bug that seems to affect all create-next-app output currently is fixed in the Tailwind templates.

Would recommend adjusting the styles in the default templates as well to get rid of this bug but that would be a separate PR.

@ctjlewis ctjlewis force-pushed the create-next-app-tailwind branch from 6f6f51d to c62a0dc Compare May 2, 2021 15:09
@ctjlewis ctjlewis changed the title feat(create-next-app): update install process, add support for --typescript, --tailwind options feat(create-next-app): update install process, add support for --tailwind options Aug 14, 2021
kodiakhq bot pushed a commit that referenced this pull request Sep 24, 2021
Fixes #27770.

Projects initialized with `create-next-app` should not only be totally functional in terms of styling, but ideally there would be as minimal CSS as possible to serve as a "jumping off point" for users (related: #24709, where I initially proposed this fix).

However, minor issues with the specific styling approaches in the template created by `create-next-app` break the page for small viewports. This is caused by an improper use of `display: flex` in conjunction with `min-height: 100vh` on the `.container` class, and I imagine it places a drag on the initial user experience due to the fact that every user who creates a project with `create-next-app` must *independently fix* this viewport sizing issue themselves.

For example, the top of the viewport on a small display (here, iPhone SE):

![image](https://user-images.githubusercontent.com/1657236/129430078-229d5fab-b719-458c-8a94-10fb8be3490d.png)

Notice the "Welcome to Next.js" message is missing: Its container is larger than the viewport, and is set to be a flex column with `justify-center`—so we are staring at the center of the container. To demonstrate better, here is the full page render for before and after this PR:

![image](https://user-images.githubusercontent.com/1657236/129430409-52134198-651a-4cf8-915d-68b699febd77.png)

This PR adjusts the styling to fix this issue, and also other styling issues on small screens, like grid width issues causing text to overflow (and the footer padding as seen above):

![image](https://user-images.githubusercontent.com/1657236/129430114-1dda7674-3b02-45d4-a4b3-37fc5053c6c4.png)

After these changes, and minor padding tweaks, this is the top of the viewport on an iPhone X (full render above):

![image](https://user-images.githubusercontent.com/1657236/129430224-1991c1a6-8c7e-4246-b4a5-44919fb850c6.png)

And on an iPhone SE:

![image](https://user-images.githubusercontent.com/1657236/129430259-4408c52f-6fc6-4f22-9cc6-bbdbbe8d7a1a.png)
natew pushed a commit to natew/next.js that referenced this pull request Feb 16, 2022
Fixes vercel#27770.

Projects initialized with `create-next-app` should not only be totally functional in terms of styling, but ideally there would be as minimal CSS as possible to serve as a "jumping off point" for users (related: vercel#24709, where I initially proposed this fix).

However, minor issues with the specific styling approaches in the template created by `create-next-app` break the page for small viewports. This is caused by an improper use of `display: flex` in conjunction with `min-height: 100vh` on the `.container` class, and I imagine it places a drag on the initial user experience due to the fact that every user who creates a project with `create-next-app` must *independently fix* this viewport sizing issue themselves.

For example, the top of the viewport on a small display (here, iPhone SE):

![image](https://user-images.githubusercontent.com/1657236/129430078-229d5fab-b719-458c-8a94-10fb8be3490d.png)

Notice the "Welcome to Next.js" message is missing: Its container is larger than the viewport, and is set to be a flex column with `justify-center`—so we are staring at the center of the container. To demonstrate better, here is the full page render for before and after this PR:

![image](https://user-images.githubusercontent.com/1657236/129430409-52134198-651a-4cf8-915d-68b699febd77.png)

This PR adjusts the styling to fix this issue, and also other styling issues on small screens, like grid width issues causing text to overflow (and the footer padding as seen above):

![image](https://user-images.githubusercontent.com/1657236/129430114-1dda7674-3b02-45d4-a4b3-37fc5053c6c4.png)

After these changes, and minor padding tweaks, this is the top of the viewport on an iPhone X (full render above):

![image](https://user-images.githubusercontent.com/1657236/129430224-1991c1a6-8c7e-4246-b4a5-44919fb850c6.png)

And on an iPhone SE:

![image](https://user-images.githubusercontent.com/1657236/129430259-4408c52f-6fc6-4f22-9cc6-bbdbbe8d7a1a.png)
Copy link
Member

@ijjk ijjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR! Seems this is a bit stale now so I'm gonna close it for now, also I'm not sure we want to add a --tailwind flag as it's equivalent to --example=tailwind but maybe the alias does make sense. We can investigate this further on an idea discussion though!

@ijjk ijjk closed this May 29, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
create-next-app Related to our CLI tool for quickly starting a new Next.js application.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants