Giter Site home page Giter Site logo

nextjs-routes's People

Contributors

airtonix avatar akanoca avatar alexgorbatchev avatar asamsig avatar elindorath avatar mariasolos avatar mcapodici avatar po4tion avatar sachinraja avatar tatethurston avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

nextjs-routes's Issues

Generated types are prefixed with "pages/"

My pages folder is in the project root. The generated types file looks like this:

export type Route =
    | { pathname: "pages/_app"; query?: Query | undefined }
    | { pathname: "pages/_document"; query?: Query | undefined }
    | { pathname: "pages/_error"; query?: Query | undefined }

hence, I get type errors as the correct paths don't have the pages/ prefix.

Typing next/navigation

Apologies for multiple issues this week. Trying to type all the things with the new next13.

I notice that useRouter is typed only from the next/link import and not the new next/navigation module, and I was wondering if it will be supported in the future.

Thanks for the great package!

Router query path params are incorrectly typed

If a page is statically optimized, meaning it is pre-rendered on the server, the router's query object will be empty when the component first renders (hydration pass). Only after hydration, Next.js triggers an update to provide the route parameters in the query object.

As a consequence, the following query object should be typed as { foo: string | undefined } as opposed to { foo: string }.

import { useRouter } from "next/router";

// query is now typed as `{ foo: string }`
const { query } = useRouter<"/foos/[foo]">();

Next.js does this because React expects that there is no difference between the React tree that was pre-rendered and the React tree that was rendered during the first render (hydration) in the Browser. See React docs for more.

If a page uses getServerSideProps and getInitialProps, the query object won't be empty in the first render. See Automatic Static Optimization for more on this.

The router provides the value isReady to check if the router fields have been updated client-side and are ready for use. So ideally, checking for isReady should type narrow the query values:

import { useRouter } from "next/router";

const { query, isReady } = useRouter<"/foos/[foo]">();
const { foo } = query;
// `foo` is `string | undefined`

if (isReady) {
	// `foo` is now `string`
	functionThatExpectsStringParamater(foo);
}

I have reproduced the type error in this repository: https://github.com/IGassmann/query-type-error

If you open the file pages/[name].tsx, you won't see any type error for the function call expectStringParameter(name);, however, when you run the page, you will get a runtime error for the type of name. This happens because the name query value is incorrectly typed by nextjs-routes.

Change locale and stay on the same route

Hi,

I tried to change the locale and stay on the same route by following nextjs exemple.

const router = useRouter()
const { pathname, asPath, query } = router
// change just the locale and maintain all other route information including href's query
router.push({ pathname, query }, asPath, { locale: nextLocale })

Unfortunately I have a typescript error with nextjs-routes.

I fixed it with an assertion any but maybe you have a better solution?

Thanks for your help!

Remove npx nextjs-routes

What

Remove cli invocation of nextjs-routes: eg npx nextjs-routes and yarn nextjs-routes.

Direct users to use automatic wiring instead:

+ const { withRoutes } = require("nextjs-routes/next-config.cjs");

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

- module.exports = nextConfig;
+ module.exports = withRoutes(nextConfig);

Why

This enables richer configuration in the future, leveraging withRoutes to supply any configuration options.

Rollout

A deprecation notice will log for usage of npx nextjs-routes and yarn nextjs-routes, directing to this issue. Any objects to this change can be raised on this issue. After a period of time if no persuasive objections are raised, this usage will removed from the package.

Typed locales if i18n is configured

The standard type for locale, for example in const { locale } = useRouter()is always string|undefined.

undefined is only applicable, if i18n is not configured.

What do you mean, is it in the scope of the package that locale and locales are typed properly related to the configuration in the next.config.js?

A config which looks like this...

{
module.exports = {
  i18n: {
    localeDetection: true,
    defaultLocale: 'de-DE',
    locales: ['de-AT', 'de-DE', 'de-CH', 'en-FR', 'en-IT', 'en-GB', 'en-US']
  }
}

... would result into a type override which looks like this:

declare module 'next/router' {
  import type { NextRouter as NextBaseRouter } from 'next/dist/client/router'
  export * from 'next/router'
  export { default } from 'next/router'

  type NextRouter = Omit<NextBaseRouter, 'locale'> & {
    locale: ['de-AT', 'de-DE', 'de-CH', 'en-FR', 'en-IT', 'en-GB', 'en-US']
  }

  export type { NextRouter }

  export function useRouter(): NextRouter
}

This is just a quick draft. We need to dig deeper into the package as, unfortunately, locale is typed on many code pieces in the routing types.

Looking forward for feedback about the idea. Maybe an v1 feature as well...

Improve Link type for better inference

I am using Next.js with nextjs-routes together with Mantine. Mantine has a component Menu.Item where all its props are inferred by a component prop. Unfortunately, this fails when setting component={Link} as it always infers that the href of Link is only of type Query (and doesn't consider Route and StaticRoute).

Currently, Link is defined this way:

  declare function Link(
    props: PropsWithChildren<LinkProps<Route>>
  ): LinkReactElement;
  declare function Link(
    props: PropsWithChildren<LinkProps<StaticRoute>>
  ): LinkReactElement;
  declare function Link(
    props: PropsWithChildren<LinkProps<Query>>
  ): LinkReactElement;

  export default Link;

But the issue would be fixed if it would be defined this way:

  declare function Link(
    props: PropsWithChildren<LinkProps<Route | StaticRoute | Query>>
  ): LinkReactElement;

  export default Link;

I can't think of any drawbacks, but I am not a TS expert.

Server side redirect return

We are using redirects for cases when data is not found or not present. Any way here to also type the source and destination parts for the redirect return?

For example

export const getServerSideProps: GetServerSideProps<DetailsPageProps> = async (context) => {
  const result = some fetching

  const data = result.data;

  if (!data) {
    return {
      redirect: {
        source: '/data/[id]',
        destination: '/',
        permanent: false,
      },
    };
  }

  return {
    props: {
      data,
    },
  };
};

Unable to resolve nextjs-routes/config though working

Hi,
First things first, thank you for this great tool! Amazing and accurate work.

I'm facing a dependency resolution issue that's keeping me from building the project without having to comment / disable the nextjs-routes require line.

Important to note that although the module is not resolved and ESLinting showing an error, it still works perfectly and generates routes at the right location as expected.

Any clue?

Thanks!

image

Make hardcoded part a template?

Hi!

I was glad to find this module, and at first, it worked pretty well. But when I started to build our code, which is a part of a bigger thing, I ran into a problem with that pathname is too strict and doesn't allow other strings, which may occur outside of the project src files.

E.g. this:

    ...(router.pathname === "/dashboard" && ...

makes an error:

Type error: This condition will always return 'false' since the types '"/404" | "/[id]" | "/" | ... and '"/dashboard"' have no overlap.

I can only edit the generated file now like this, to allow for string checks:

export interface NextRouter<P extends Pathname = Pathname>
    extends Omit<Router, "push" | "replace"> {
    pathname: P | string; /// <<<<<< ` | string` is added HERE
    route: P;
    query: QueryForPathname[P];
    ...
  }

but it's gonna be rewritten.

I'd propose to use an overridable template instead. What do you think?

Add option for same pathname but new hash

Currently, these won't type properly if you wanted the equivalent of href="#some-id"

const id = "some-id"
<Link
  href={`${id}`}
/>

or

<Link
  href={{
    hash: id
  }}
/>

Should just need to union a hash only type:

type Hash = { hash: string }

export interface LinkProps ... {
  // Union `Hash` here
  href: Route | StaticRoute | Query | Hash
  ...
}

Add support for Next.js app structure

Next.js 13 is now out and it released the new layout system under the new app folder.

Is it something this package is interested in adopting?

The route structure is slightly different in that folder and both app and pages can be used at the same time so the tool would have to be able to merge the two route structures.

Allow href in Links to be a string

Since Next allows href to be a string in Links, it would be nice to also offer this more compact syntax (e.g.: Allow users to write href="/" instead of href={{ pathname: '/' }}). We can still make this typed so that only the corresponding string literals are allowed.

If this sounds like a good idea, I can create the PR with the implementation :)

Generate .d.ts file during build also

It seems that only next dev generates the file, but next build does not, so if I .gitignore the generated typings, build fails on CI.

I read in the readme that the route typings are generated during build also, but cannot reproduce this.

to reproduce

  • Delete the nextjs-routes.d.ts file
  • Run next build
  • typings are not created

Since typings are not created, the typescript build fails if we

import type { Route } from "nextjs-routes";

expected

The file is also generated during build

Use with getServerSideProps, params may be undefined

I am using:

  • "next": "13.2.4"
  • "nextjs-routes": "^1.0.8"

I have the following file:

pages/automator-configs/[label].tsx:

import { GetServerSideProps, InferGetServerSidePropsType } from 'next/types';
import { getAutomatorConfig } from '../../ansibleAdapter/automatorConfig';
import type { RoutedQuery } from 'nextjs-routes';

export const getServerSideProps: GetServerSideProps<{}, RoutedQuery<'/automator-configs/[label]'>> = async (
  context,
) => {
  const { label } = context.params;
  return { props: { automatorConfig: getAutomatorConfig()[label] } };
};

export default function AutomatorConfig({ automatorConfig }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return <pre className="max-h-96">{JSON.stringify(automatorConfig, null, 2)}</pre>;
}

However, this shows several eslint and TypeScript errors:

For the first generic in GetServerSideProps, eslint shows: “Don't use {} as a type. {} actually means "any non-nullish value"”. I guess this can be ignored for now.

context.params is resolved as:

(property) params?: ({
    label: string;
} & Query) | undefined

so I cannot do const { label } = context.params;, because it shows:

Property 'label' does not exist on type '({ label: string; } & Query) | undefined'.ts(2339)

In my nextjs-routes.d.ts, I see:

declare module "nextjs-routes" {
  export type Route =
    | StaticRoute<"/404">
    | DynamicRoute<"/automator-configs/[label]", { "label": string }>
    | StaticRoute<"/">;

  interface StaticRoute<Pathname> {
    pathname: Pathname;
    query?: Query | undefined;
    hash?: string | null | undefined;
  }

  interface DynamicRoute<Pathname, Parameters> {
    pathname: Pathname;
    query: Parameters & Query;
    hash?: string | null | undefined;
  }

  interface Query {
    [key: string]: string | string[] | undefined;
  };

  export type RoutedQuery<P extends Route["pathname"]> = Extract<
    Route,
    { pathname: P }
  >["query"];

  export type Locale = undefined;

  /**
   * A typesafe utility function for generating paths in your application.
   *
   * route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
   */
  export declare function route(r: Route): string;
}

So it makes sense that the query would also possibly be undefined. However, this is not what's shown in the docs.

What would be the recommended way to solve this issue?

ability to define what query params can exist on each path

This is more of something that would take nextjs-routes to the next level for us, but it's already a great solution. It would be nice if we could augment the query types for each route to add our own. For example, defining a query param called edit on the /user/[id] route to change the page to edit mode.

This is just an feature request right now, I'll try to think of some ideas on how we could do this later.

nxdev, Could not find a Next.js pages directory.

because it is assumed that the plugin will find the app or pages directory relative to process.cwd(), this plugin doesn't work in a nxdev monorepo.

The solution is to:

  • accept a direct path to the project root.

edit:

  • update the docs, change cwd for dir (or update the code to talk about cwd)
  • generate the types under dir (edit: solved with outdir)

Question: Other query parameters

Hi,

We're using this library and it's great for mapping the slug parts of a path to their required values or query parameters. However we also using actual query string values for certain items and it appears as though that isn't possible here without the types throwing an error.

Any recommended way to do something like this?

              <Link
                href={{
                  pathname: '/api//foo/[id]',
                  query: {
                    id: 'bar',
                    mode: 'baz', // this complains
                  },
                }}
              >
                <a>A Thing</a>
              </Link>

To me it would be nice to change the query value to params and then just let people merge in their own query parameters in an un-type-safe kind of way. Otherwise people are prevented from making use of some pretty important browser functionality.

V1

Now that the API has begun to stabilize, we should consider what (if anything) should be added before releasing a v1. That release will mark the adoption of semantic versioning, to make it easier for users to reason about updates.

Semicolons are used where a comma should be used

For me, the generated output has ;s instead of ,s to separate object entries which is incorrect and errors.

export type Route =
    | { pathname: "/api/dataset/[datasetId]/[datasetVersion]"; query: Query<{ datasetId: string; datasetVersion: string }> }
    | { pathname: "/api/project/[projectId]/file"; query: Query<{ projectId: string }> }
…

using v0.0.18

?query=string - how to?

I'm trying to understand how nextjs-route can handle ?query=part?
For example, we have a page with a table that is supposed to be paged. So I need to pass count and page for every URL.
But nextjs-routes Router doesn't know about our params and I get errors.

Cannot find module

Screenshot 2023-01-19 at 12 45 03

VSCode seems no to be finding the types required for the package, pnpm add @types/nextjs-routes package doesn't exist as well

extract route traversin/parsing webpack plugin to separate module

You have implemented a good module which can be used for more potential use cases.
I personally would like to have it for making custom navigation that will be auto generated from available routes.
I see that you can have an can pass a callback option to NextJSRoutesPlugin.
This callback will accept parsed data from routes and will do code generation.

class NextJSRoutesPlugin implements WebpackPluginInstance {

  apply() {
    // ...
    if (this.context.dev) {
      const watcher = watch(watchDirs, {
        persistent: true,
      });
      // batch changes
      const generate = debounce(() => this.processRoutes, 50);
      watcher.on("add", generate).on("unlink", generate);
    } else {
      this.processRoutes();
    }
  }
  collectRoutes() {
    const defaultOptions = {
      // ...
    };
    const opts = {
      ...defaultOptions,
      ...this.options,
    };
    // ...
    return nextRoutes(files);
  }
  processRoutes: () => {
    this.options.callback(this.collectRoutes)
  }
}
  config.plugins.push(
    new NextJSRoutesPlugin(nextConfig, context, {
      ...options,
      callback: (routes) => {
        const generated = generate(routes, options);
        writeFileSync(outputFilepath, generated);        
      }
    })
  );

Strategy for evaluating page files or reading their AST

Next's documentation notes the following:

During prerendering, the router's query object will be empty since we do not have query information to provide during this phase. After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.

To ensure type safety, nextjs-routes assumes every page is optimized. This requires that clients always use router.isReady to narrow the router.query type when using useRouter.

If we import/require the transpiled page files (or read the AST), we can detect whether a page will be optimized. This will also unblock #132 and possibly #39.

A few considerations:

  • We may need to use the user's webpack configuration, to handle arbitrary ast extensions (JS/TS/JSX/TSX are examples). Those examples may be sufficient coverage, rather than supporting any possible ast extension.
  • In development, next lazily transpiles source files, so we may not have the full route picture. We shouldn't force evaluation of all page files, instead any code generation should likely become iterative. This will require updating the generated file via ast tooling rather than rewriting the whole file.

There will be some tradeoffs here wrt complexity, we may need to investigate a few different solutions to minimize the amount of complexity this package takes on.

[Question] Non-Link usage

I wanted to use this in a non-link. For example a fetch like fetch('/foo'), is there anyway to typecheck that foo?

I was thinking of doing a type like:

const ResourcePath = Parameters<typeof useRouter>[0]['path'];
const path: ResourcePath = '/foo';
fetch(path);

But it's kind of extra to create a variable to hold path then type that.

Support for Custom Page Extensions

Hi there,

Using v0.1.0, I noticed that it doesn't seem to support Custom Page Extensions. Indeed, the generated nextjs-routes.d.ts file defines a route for every files found in the pages (or src/pages) directory, including non page ones.

I think it might be something you want to consider for #44.

Feel free to ask me anything that can help.

Thanks for this tool, Vercel should take inspiration from it!

Nextjs@13 not supported dynamic href in the /app directory

First, If you are considering this, I am sorry for registering the issue.

When I run the examples/app folder(nextjs version is 13.3.0), I get the following error.

image

Link: Next.js Error Occurred

If you go to the link provided above, next.js says that url mapping is no longer available for the app directory structure.

[example code]

<Link href={{ pathname: "/[store]", query: { store: "tate" } }}>
  Tate's Store
</Link>

I think we need to figure out a way to use it in the app directory of Next.js@13.

If there's anything I made a mistake, please give me some advice!

DynamicRoute with getStaticPaths -> StaticRoute

For dynamic routes that export getStaticPaths it would be useful to have a config setting to enable automatic translation from DynamicRoute -> StaticRoute.

You definitely want this off by default in case it's an expensive operation

Example

Give the page/pathname:

 "/blog/[slug]"

which exports getStaticPaths:

const getStaticPaths = () => ({
  paths: ["1", "2", "3"]
})

Now, instead of just:

<DynamicRoute<"/blog/[slug]", { "slug": string }>

You get:

<DynamicRoute<"/blog/[slug]", { "slug": string }>
<StaticRoute<"/blog/1">
<StaticRoute<"/blog/2">
<StaticRoute<"/blog/3">

Note

One small detail is that if you have fallback: false

const getStaticPaths = () => ({
  paths: ["1", "2", "3"],
  fallback: false
})

The DynamicRoute should be omitted:

<StaticRoute<"/blog/1">
<StaticRoute<"/blog/2">
<StaticRoute<"/blog/3">

SyntaxError: Unexpected token 'export' when test with jest

Hello,

I have Nextjs 13.1.6 and Jest for building my app.

but while testing I use function route() from nextjs-routes it cause error and make test suite failed.

this is some sample error I got

/node_modules/.pnpm/[email protected][email protected]/node_modules/nextjs-routes/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export function route(r) {
                                                                                                                                                      ^^^^^^

    SyntaxError: Unexpected token 'export'

How I can solve this problem thank you :)

Allow generating types outside of expo dev server.

I'd like to be able to manually generate the nextjs-routes.d.ts file instead of relying on the file watcher on the nextjs dev server. The main reason for this is so we can manually generate types in CI, but we also have some nextjs config which appears to break when we use withRoutes() in next.config.js. Additionally, we have other codegen in our repo, so we'd like to be able to have one script that does all of it at once.

What I'd like to do is to call the function writeNextjsRoutes manually in a script, but it's not exported so I can't use it. It would be even better if I could run a command like yarn nextjs-routes codegen to generate types, but I'll settle for the easiest possible soution.

Thanks! Love this project. I'm excited to be able to use it.

Hash in `Link`'s `href` is not allowed

Hi there,

As I build up my codebase, I came on the use case of having a link with a hash to open a page scrolled on a specific id. While the original next/link component supports it (with the help of the scroll prop), the routes type definition generated by nextjs-routes prevents it as there is no hash possible in pathname.

I think this could be addressed with template literal string, but I wonder what do you think of it?

Error when using custom Link component

I am getting this error when I use my custom Link component Type 'string' is not assignable to type 'Route | Query' but It works with default Link component from next js. I am doing something wrong?

this is my custom Link component

import NextLink, { LinkProps as NextLinkProps } from 'next/link'

export type LinkProps = Omit<NextLinkProps, 'passHref' | 'as'> & Omit<ChakraLinkProps, 'href'>

export const Link: React.FC<React.PropsWithChildren<LinkProps>> = ({
  children,
  href,
  onClick,
  className,
  replace,
  scroll,
  shallow,
  prefetch,
  locale,
  ...props
}) => {
  return (
    <NextLink href={href} legacyBehavior passHref>
      <ChakraLink {...props}>{children}</ChakraLink>
    </NextLink>
  )
}

When I use it like this I will get error I mentioned above but when I import Link from next js everything works as expected.

<Link href="/users">text</Link>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.