GioJSdocs
On this page

gio migrate

Migrate a Next.js project (pages or app router) to GioJS in place, by running create-giojs migrate, and write MIGRATION_REPORT.md.

npx gio migrate ./my-next-app --dry-run
bash
gio migrate [dir] [--dry-run] [-y] [--config <file>]

# the same command, without @gio.js/server installed:
npm create giojs@latest -- migrate [dir]
npx create-giojs migrate [dir]
npx -p create-giojs gio-migrate [dir]

Reference

OptionTypeDefaultDescription
[dir]path.The Next.js project root.
--dry-run, -nbooleanfalsePrint the plan and a unified diff of every change; write nothing.
-y, --yesbooleanfalseApply without asking. Without a terminal (CI, piped input) a real run needs it.
--config <file>path-Only convert this next.config file to gio.toml (written next to it). Takes --dry-run too.
-h, --helpboolean-Print create-giojs migrate's help and exit with 0.

Behavior

gio migrate passes its arguments to create-giojs migrate, which owns the transforms. It runs:

  1. the create-giojs installed in the project or next to @gio.js/server;
  2. otherwise create-giojs@<your gio version> through your package manager (npx, pnpm dlx or bunx), so the CLI and the migration are the same release. It prints gio migrate: create-giojs is not installed, running npx create-giojs@... first.

gio never runs a create-giojs to find out whether it knows migrate: releases from before the subcommand treat any unknown argument as the name of a new project and scaffold it. It reads the subcommand from create-giojs's package.json exports (create-giojs/migrate) instead - from disk for an installed copy, from the registry with npm view (which downloads and runs nothing) otherwise. A release without it is reported, with the install command, and not run.

The migration itself:

  • Plans every change first and prints a summary. A real run then warns when the directory is not a git repository or has uncommitted changes, and asks before writing (or needs --yes).
  • Moves pages/ to app/ (pages/about.tsx → app/about/page.tsx, pages/api/x.ts → app/api/x/route.ts, _app / _document into app/layout.tsx), and src/app to app/.
  • Rewrites next/link, next/image, next/router, next/navigation, next/head, next/script, next/dynamic, next/font and next/cache code, and turns getStaticProps into getServerSideProps plus export const revalidate. Anything it cannot convert gets a // TODO(gio-migrate): comment.
  • Converts next.config redirects, rewrites, headers, images and i18n to gio.toml, merged into an existing one only when that is safe (otherwise gio.migrated.toml).
  • Swaps next for the @gio.js/* packages in package.json and sets "type": "module".
  • Writes MIGRATION_REPORT.md with every move, change and TODO, by file and line.

Examples

Preview the migration

text
$ npx gio migrate ./next-app --dry-run
Next.js project (pages router) at /home/me/next-app

  → pages/api/hello.ts → app/api/hello/route.ts  (2 TODOs)
  → pages/index.tsx → app/page.tsx  (2 changes)
  → pages/posts/[id].tsx → app/posts/[id]/page.tsx  (2 changes, 1 TODO)
  + gio.toml
  ~ package.json  (11 changes)
  + MIGRATION_REPORT.md

4 TODOs for you - see MIGRATION_REPORT.md

--- pages/api/hello.ts
+++ app/api/hello/route.ts
...

Dry run - nothing was written.

→ is a move, + a new file, ~ an edit in place.

Apply without a prompt

bash
git switch -c migrate-to-giojs
npx gio migrate . --yes
npm install && npx tsc --noEmit && npm run dev

Without --yes and without a terminal, nothing is written:

text
Refusing to modify files without confirmation: re-run with --yes to apply, or --dry-run to preview.

Convert only next.config

text
$ npx gio migrate --config next.config.js --dry-run
  ✔ redirect /old/:path* → /new/:path* → [[redirects]] /old/*path → /new/*path (308)
--- gio.toml
+++ gio.toml
@@ -0,0 +1,16 @@
+# gio.toml - generated by create-giojs migrate from next.config.
+# Reference: https://giojs.com/docs/configuration
+
+[app]
+name = "next-app"
...
+[[redirects]]
+from = "/old/*path"
+to = "/new/*path"
+status = 308

Dry run - nothing was written.

Good to know

  • Commit first. The migration edits files in place and is meant to be reviewed as a diff; a file is never moved onto an existing one.
  • Exit codes come from create-giojs migrate: 0 when it applied, previewed, or you answered no; 1 for an error or a refused run without --yes; 2 for a usage error (an unknown option, a second directory, --config without a file), as every gio usage error. gio migrate exits 1 itself when no suitable create-giojs can be found or run.
  • A new gio.toml names the app after the name in package.json (with --config, the one next to the config file), or my-app when there is none. --config refuses to overwrite an existing gio.migrated.toml.
  • gio help migrate prints gio's summary; gio migrate --help prints the full help of create-giojs migrate.

Version history

VersionChanges
v0.1.0-beta.8Introduced. The migration (also create-giojs migrate and the gio-migrate bin) parses code with the TypeScript compiler, replacing the regex codemod. Usage errors exit with 2, and --config names the app after package.json like the full migration.