Skip to content

Commit

Permalink
Fixed the issue with lib dir being ignored.
Browse files Browse the repository at this point in the history
  • Loading branch information
Miloš Brajević committed Nov 6, 2019
1 parent 6e256af commit 1bf9243
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,4 @@ dist

# Build-generated files
es
lib
cjs
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"name": "next-universal-route",
"version": "0.7.0",
"version": "0.7.1",
"description": "Universal Next.js Route",
"main": "./lib/index.js",
"main": "./cjs/index.js",
"module": "./es/index.js",
"modules.root": "./es",
"types": "./lib/index.d.ts",
"types": "./cjs/index.d.ts",
"author": "Miloš Brajević <[email protected]>",
"repository": "brajevicm/next-universal-route",
"license": "MIT",
"scripts": {
"lint": "tslint -c tslint.json --project tsconfig.json",
"test": "jest --config jest.config.json && codecov",
"test:watch": "jest --config jest.config.json --watchAll",
"build": " rimraf ./dist && npm run build:es && npm run build:lib",
"build": " rimraf ./dist && npm run build:es && npm run build:cjs",
"build:es": "tsc --module es2015 --target es5 --outDir es",
"build:lib": "tsc --module commonjs --target es5 --outDir lib"
"build:cjs": "tsc --module commonjs --target es5 --outDir cjs"
},
"keywords": [
"react",
Expand Down
36 changes: 36 additions & 0 deletions src/lib/Link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { Children, ReactChildren } from 'react';
import NextLink from 'next/link';

import { NextRoute } from './NextRoute';

type LinkProps = {
href: NextRoute | string;
replace?: boolean;
scroll?: boolean;
shallow?: boolean;
passHref?: boolean;
prefetch?: boolean;
children?: ReactChildren;
};

export const Link = (props: LinkProps) => {
const { href, ...rest } = props;
const newHref = typeof href === 'string' ? href : href.toHref();

if (typeof href === 'string' || href.isAbsolutePath) {
const child: any = Children.only(props.children);
const { children, ...newRest } = rest;

if (props.passHref || (child.type === 'a' && !('href' in child.props))) {
return React.cloneElement(child, { href: newHref, ...newRest });
}

return (
<a href={newHref} {...newRest}>
{children}
</a>
);
}

return <NextLink href={href.toHref()} as={href.toAs()} {...rest} />;
};
52 changes: 52 additions & 0 deletions src/lib/NextRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { stringify } from 'qs';
import { generatePath } from '../utils/generatePath';
import { isAbsolutePath } from '../utils//isAbsolutePath';

export class NextRoute {
public path: string;
public page?: string;
public params?: object;
public queryStringParams?: object;
public query?: object;
public isAbsolutePath: boolean;

constructor(
path: string,
page?: string,
params?: object,
queryStringParams?: object,
query?: object
) {
this.path = path;
this.page = page;
this.params = params;
this.queryStringParams = queryStringParams;
this.query = query;
this.isAbsolutePath = isAbsolutePath(path);
}

public toAs(): string {
if (this.isAbsolutePath) {
return this.path;
}

const path = generatePath(this.path, this.params);
const queryString = stringify(this.queryStringParams);

return queryString ? `${path}?${queryString}` : path;
}

public toHref(): string {
if (this.isAbsolutePath) {
return this.path;
}

const queryString = stringify({
...this.query,
...this.params,
...this.queryStringParams
});

return queryString ? `${this.page}?${queryString}` : this.page;
}
}
119 changes: 119 additions & 0 deletions src/lib/Route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import pathToRegexp from 'path-to-regexp';
import { parse } from 'url';

import { NextRoute } from './NextRoute';
import { isFunction } from '../utils/isFunction';
import { mapValues } from '../utils/mapValues';
import { formatUrl } from '../utils/formatUrl';

export class Route {
public path: string;
public page?: string;
private _query: object;
private urlFormatter?: Function;
private params: object;
private queryStringParams: object;

constructor(path: string, page?: string, urlFormatter?: Function) {
this.path = path;
this.setPage(`/${page}`);
this.urlFormatter = urlFormatter;
this.params = {};
this.queryStringParams = {};
}

get query() {
return { ...this._query, ...this.queryStringParams };
}

public generateUrl(params: object = {}, queryStringParams?: object) {
const newParams = this.formatUrl({ ...this.params, ...params });
const newQueryStringParams = this.formatUrl({
...this.queryStringParams,
...queryStringParams
});

return new NextRoute(
this.path,
this.page,
newParams,
newQueryStringParams,
this._query
);
}

public generateFromUrl(url: string, params: object): NextRoute {
const { pathname, query } = this.parseUrl(url);
const keys = [];
const regex = pathToRegexp(this.path, keys);
const values = regex.exec(pathname);

const newParams = this.getQuery(values.slice(1), keys);
const queryStringParams = {
...this.queryStringParams,
...query,
...params
};

return this.generateUrl(newParams, queryStringParams);
}

public isMatch(url: string) {
const { pathname, query } = this.parseUrl(url);

const keys = [];
const regex = pathToRegexp(this.path, keys);
const isMatch = regex.test(pathname);

if (isMatch) {
const values = regex.exec(pathname);

this._query = {
...this._query,
...this.getQuery(values.slice(1), keys)
};

this.queryStringParams = query;
}

return isMatch;
}

private setPage(url: string): void {
const { pathname, query } = this.parseUrl(url);

this._query = query;
this.page = pathname;
}

private formatUrl(params: object): object {
let fn: Function = formatUrl;

if (isFunction(this.urlFormatter)) {
fn = (string: string) => formatUrl(this.urlFormatter(string));
}

return mapValues(params, (param: string | number) =>
typeof param === 'string' ? fn(param) : param
);
}

private getQuery(values, keys) {
return values.reduce((params, val, i) => {
return {
...params,
[keys[i].name]: decodeURIComponent(val)
};
}, {});
}

private parseUrl(url: string) {
const parsedUrl = parse(url, true);
const { pathname, query } = parsedUrl;

return {
pathname,
query
};
}
}
46 changes: 46 additions & 0 deletions src/lib/Router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import NextRouter, { NextRouter as NextRouterType } from 'next/router';

import { Route } from './Route';
import { NextRoute } from './NextRoute';

// import { clone } from './lib/deepClone';

// TODO: Find the way to replace Next's Router completely
// const ClonedRouter = ((router: NextRouterType) => {
// const newRouter = clone(router);

// newRouter.push = (href: NextRoute, options?: object) =>
// router.push(href.toHref(), href.toAs(), options);

// newRouter.prefetch = (href: NextRoute) => router.prefetch(href.toHref());

// newRouter.replace = (href: NextRoute, options?: object) =>
// router.replace(href.toHref(), href.toAs(), options);

// return newRouter;
// })(NextRouter);

export const Router = ((router: NextRouterType) => {
const push = (href: NextRoute, options?: object) =>
router.push(href.toHref(), href.toAs(), options);

const prefetch = (href: NextRoute) => router.prefetch(href.toHref());

const replace = (href: NextRoute, options?: object) =>
router.replace(href.toHref(), href.toAs(), options);

const update = (href: Route, params: object) =>
push(
href.generateFromUrl(
`${window.location.pathname}${window.location.search}`,
params
)
);

return {
push,
prefetch,
replace,
update
};
})(NextRouter);
13 changes: 13 additions & 0 deletions src/lib/Routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Route } from './Route';

export class Routes {
private routes: Route[];

constructor(routes) {
this.routes = Object.values(routes);
}

public getRoute(url: string) {
return this.routes.filter(route => route.isMatch(url))[0];
}
}
16 changes: 16 additions & 0 deletions src/lib/getRequestHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Routes } from './Routes';

export const getRequestHandler = (app, routes) => {
const nextHandler = app.getRequestHandler();
const router = new Routes(routes);

return (req, res) => {
const route = router.getRoute(req.url);

if (route) {
app.render(req, res, route.page, route.query);
} else {
nextHandler(req, res, req.url);
}
};
};

0 comments on commit 1bf9243

Please sign in to comment.