Skip to content

Commit fb38bd3

Browse files
committed
feat: revamp next generator according to new dynamic routing
1 parent 264efc1 commit fb38bd3

File tree

16 files changed

+70
-239
lines changed

16 files changed

+70
-239
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"handlebars-helpers": "^0.10.0",
4242
"isomorphic-fetch": "^2.2.1",
4343
"mkdirp": "^0.5.1",
44-
"recast": "^0.18.1",
4544
"sprintf-js": "^1.1.1"
4645
},
4746
"scripts": {

src/generators/BaseGenerator.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ export default class {
121121

122122
getType(field) {
123123
if (field.reference) {
124-
return field.reference.title;
124+
if (field.maxCardinality !== 1) {
125+
return "string[]";
126+
}
127+
128+
return "string";
125129
}
126130

127131
switch (field.range) {

src/generators/NextGenerator.js

Lines changed: 8 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import chalk from "chalk";
2-
import fs from "fs";
32
import BaseGenerator from "./BaseGenerator";
4-
import { parse, print, types } from "recast";
53

64
export default class NextGenerator extends BaseGenerator {
75
constructor(params) {
@@ -23,86 +21,22 @@ export default class NextGenerator extends BaseGenerator {
2321
"interfaces/foo.ts",
2422

2523
// pages
26-
"pages/foo.tsx",
27-
"pages/foos.tsx",
24+
"pages/foos/[id].tsx",
25+
"pages/foos/index.tsx",
2826

2927
// utils
3028
"utils/dataAccess.ts"
3129
]);
3230
}
3331

34-
checkDependencies(dir, serverPath) {
35-
const dependencies = this.getTargetDependencies(dir);
36-
37-
if (!dependencies.length) {
38-
return;
39-
}
40-
41-
if (!dependencies.includes("@zeit/next-typescript")) {
42-
console.log(
43-
chalk.yellow(
44-
"It seems next-typescript is not installed but generator needs typescript to work efficiently."
45-
)
46-
);
47-
}
48-
49-
if (!dependencies.includes("express")) {
50-
console.log(
51-
chalk.yellow(
52-
"It seems express is not installed but generator needs a custom express server to work efficiently."
53-
)
54-
);
55-
}
56-
57-
if (serverPath) {
58-
if (!fs.existsSync(serverPath)) {
59-
console.log(chalk.red("Express server file doesn't exists."));
60-
return;
61-
}
62-
63-
const { mode } = fs.statSync(serverPath);
64-
if ("200" !== (mode & parseInt("200", 8)).toString(8)) {
65-
console.log(chalk.red("Express server file is not writable."));
66-
}
67-
}
68-
}
69-
70-
checkImports(directory, imports, extension = ".ts") {
71-
imports.forEach(({ file }) => {
72-
if (fs.existsSync(directory + file + extension)) {
73-
return;
74-
}
75-
76-
console.log(
77-
chalk.yellow(
78-
'An import for the file "%s" has been generated but the file doesn\'t exists.'
79-
),
80-
file
81-
);
82-
});
83-
}
84-
85-
help(resource, dir) {
32+
help(resource) {
8633
console.log(
8734
chalk.green('Code for the "%s" resource type has been generated!'),
8835
resource.title
8936
);
90-
91-
// missing import
92-
const { imports } = this.parseFields(resource);
93-
this.checkImports(`${dir}/interfaces/`, imports);
94-
95-
// server route configuration
96-
if (!this.routeAddedtoServer) {
97-
const lc = resource.title.toLowerCase();
98-
console.log(
99-
"Paste the following route to your server configuration file:"
100-
);
101-
console.log(chalk.green(this.getShowRoute(lc)));
102-
}
10337
}
10438

105-
generate(api, resource, dir, serverPath) {
39+
generate(api, resource, dir) {
10640
const lc = resource.title.toLowerCase();
10741
const ucf = this.ucFirst(resource.title);
10842
const { fields, imports } = this.parseFields(resource);
@@ -113,7 +47,7 @@ export default class NextGenerator extends BaseGenerator {
11347
uc: resource.title.toUpperCase(),
11448
ucf,
11549
fields,
116-
formFields: this.buildFields(resource.writableFields),
50+
formFields: this.buildFields(fields),
11751
imports,
11852
hydraPrefix: this.hydraPrefix,
11953
title: resource.title
@@ -132,15 +66,16 @@ export default class NextGenerator extends BaseGenerator {
13266

13367
// copy with patterned name
13468
this.createDir(`${dir}/components/${context.lc}`);
69+
this.createDir(`${dir}/pages/${context.lc}s`);
13570
[
13671
// components
13772
"components/%s/List.tsx",
13873
"components/%s/ListItem.tsx",
13974
"components/%s/Show.tsx",
14075

14176
// pages
142-
"pages/%s.tsx",
143-
"pages/%ss.tsx"
77+
"pages/%ss/[id].tsx",
78+
"pages/%ss/index.tsx"
14479
].forEach(pattern =>
14580
this.createFileFromPattern(pattern, dir, context.lc, context)
14681
);
@@ -169,48 +104,6 @@ export default class NextGenerator extends BaseGenerator {
169104

170105
// API config
171106
this.createEntrypoint(api.entrypoint, `${dir}/config/entrypoint.ts`);
172-
173-
if (serverPath) {
174-
this.createExpressRoute(serverPath, lc, this.getShowRoute(lc));
175-
}
176-
}
177-
178-
getShowRoute(name) {
179-
return `server.get('/${name}/:id', (req, res) => {
180-
return app.render(req, res, '/${name}', { id: req.params.id })
181-
});`;
182-
}
183-
184-
createExpressRoute(path, resourceName, toInsert) {
185-
const content = fs.readFileSync(path, "utf-8");
186-
const code = parse(content);
187-
const { namedTypes } = types;
188-
189-
types.visit(code, {
190-
visitExpressionStatement: function(path) {
191-
const args = path.value.expression.arguments;
192-
if (
193-
2 === args.length &&
194-
namedTypes.Literal.check(args[0]) &&
195-
"*" === args[0].value &&
196-
namedTypes.ArrowFunctionExpression.check(args[1])
197-
) {
198-
// insert route before "*" route
199-
path.parent.value.body.splice(path.name, 0, toInsert);
200-
201-
return false;
202-
}
203-
204-
this.traverse(path);
205-
}
206-
});
207-
208-
fs.writeFileSync(path, print(code).code);
209-
console.log(
210-
chalk.green("'Show' route for %s has been added to your server"),
211-
resourceName
212-
);
213-
this.routeAddedtoServer = true;
214107
}
215108

216109
getDescription(field) {

src/generators/NextGenerator.test.js

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,6 @@ afterEach(() => {
1414
jest.resetAllMocks();
1515
});
1616

17-
describe("checkDependencies", () => {
18-
let getDependenciesSpy;
19-
let consoleSpy;
20-
21-
beforeEach(() => {
22-
getDependenciesSpy = jest
23-
.spyOn(generator, "getTargetDependencies")
24-
.mockReturnValue(["express", "@zeit/next-typescript"]);
25-
consoleSpy = jest.spyOn(console, "log").mockImplementation(() => null);
26-
});
27-
28-
test("should not warn if dependencies are installed", () => {
29-
generator.checkDependencies("");
30-
expect(consoleSpy).not.toHaveBeenCalled();
31-
});
32-
33-
test("should warn if express is not installed", () => {
34-
getDependenciesSpy.mockReturnValue(["@zeit/next-typescript"]);
35-
generator.checkDependencies("");
36-
expect(consoleSpy).toHaveBeenCalledTimes(1);
37-
expect(consoleSpy.mock.calls[0][0]).toContain("express");
38-
});
39-
40-
test("should warn if typescript is not installed", () => {
41-
getDependenciesSpy.mockReturnValue(["express"]);
42-
generator.checkDependencies("");
43-
expect(consoleSpy).toHaveBeenCalledTimes(1);
44-
expect(consoleSpy.mock.calls[0][0]).toContain("typescript");
45-
});
46-
});
47-
4817
describe("generate", () => {
4918
test("Generate a Next app", () => {
5019
const tmpobj = tmp.dirSync({ unsafeCleanup: true });
@@ -80,8 +49,8 @@ describe("generate", () => {
8049
"/error/SubmissionError.ts",
8150
"/interfaces/Abc.ts",
8251
"/interfaces/Collection.ts",
83-
"/pages/abc.tsx",
84-
"/pages/abcs.tsx",
52+
"/pages/abcs/[id].tsx",
53+
"/pages/abcs/index.tsx",
8554
"/utils/dataAccess.ts"
8655
].forEach(file => expect(fs.existsSync(tmpobj.name + file)).toBe(true));
8756

src/generators/TypescriptInterfaceGenerator.test.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,10 @@ test("Generate a typescript interface", () => {
5050

5151
expect(fs.existsSync(tmpobj.name + "/interfaces/foo.ts")).toBe(true);
5252

53-
const res = `import { FooBar } from "./foobar";
54-
55-
export interface Foo {
53+
const res = `export interface Foo {
5654
'@id'?: string;
5755
foo: any;
58-
foobar?: FooBar;
56+
foobar?: string[];
5957
readonly bar: string;
6058
id?: string;
6159
}

templates/next/components/common/ReferenceLinks.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import { NextFunctionComponent } from 'next';
21
import Link from 'next/link';
3-
import { Fragment } from 'react';
4-
import { btoa } from '../../utils/dataAccess';
2+
import { Fragment, FunctionComponent } from 'react';
53

64
interface Props {
75
items: string|string[];
86
type: string;
97
useIcon?: boolean
108
}
11-
export const ReferenceLinks: NextFunctionComponent<Props> = ({items, type, useIcon = false}) => {
9+
export const ReferenceLinks: FunctionComponent<Props> = ({items, type, useIcon = false}) => {
1210
if (Array.isArray(items)) {
1311
return (
1412
<Fragment>
@@ -19,12 +17,8 @@ export const ReferenceLinks: NextFunctionComponent<Props> = ({items, type, useIc
1917
);
2018
}
2119

22-
// to avoid routes like "/book/books/d4s5s1-qd5sd5d-qsd5qsd4sd" we prefer enconding it
23-
const id = btoa(items);
24-
const resourceName = type.toLowerCase();
25-
2620
return (
27-
<Link href={`/${resourceName}?id=${id}`} as={`/${resourceName}/${id}`}><a>
21+
<Link href={items}><a>
2822
{useIcon ? (
2923
<Fragment>
3024
<span className="fa fa-search" aria-hidden="true" />

templates/next/components/foo/List.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { NextFunctionComponent } from 'next';
1+
import { FunctionComponent } from 'react';
22
import { ListItem } from './ListItem';
33
import { {{{ucf}}} } from '../../interfaces/{{{ucf}}}';
44

55
interface Props {
66
{{{name}}}: {{{ucf}}}[];
77
}
88

9-
export const List: NextFunctionComponent<Props> = ({ {{{name}}} }) => (
9+
export const List: FunctionComponent<Props> = ({ {{{name}}} }) => (
1010
<div>
1111
<h1>{{{ucf}}} List</h1>
1212
<table className="table table-responsive table-striped table-hover">

templates/next/components/foo/ListItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { NextFunctionComponent } from 'next';
1+
import { FunctionComponent } from 'react';
22
import { {{{ucf}}} } from '../../interfaces/{{{ucf}}}';
33
import { ReferenceLinks } from '../common/ReferenceLinks';
44

55
interface Props {
66
{{{lc}}}: {{{ucf}}}
77
}
88

9-
export const ListItem: NextFunctionComponent<Props> = ({ {{{lc}}} }: Props) => (
9+
export const ListItem: FunctionComponent<Props> = ({ {{{lc}}} }: Props) => (
1010
<tr>
1111
<th scope="row"><ReferenceLinks items={ {{{lc}}}['@id'] } type="{{{lc}}}" /></th>
1212
{{#each fields}}

templates/next/components/foo/Show.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NextFunctionComponent } from 'next';
1+
import { FunctionComponent } from 'react';
22
import Link from 'next/link';
33
import { ReferenceLinks } from '../common/ReferenceLinks';
44
import { {{{ucf}}} } from '../../interfaces/{{{ucf}}}';
@@ -7,7 +7,7 @@ interface Props {
77
{{{lc}}}: {{{ucf}}};
88
}
99

10-
export const Show: NextFunctionComponent<Props> = ({ {{{lc}}} }) => (
10+
export const Show: FunctionComponent<Props> = ({ {{{lc}}} }) => (
1111
<div>
1212
<h1>Show { {{{lc}}}['@id'] }</h1>
1313
<table className="table table-responsive table-striped table-hover">
@@ -18,10 +18,6 @@ export const Show: NextFunctionComponent<Props> = ({ {{{lc}}} }) => (
1818
</tr>
1919
</thead>
2020
<tbody>
21-
<tr>
22-
<th scope="row">title</th>
23-
<td>{ {{{lc}}}.title }</td>
24-
</tr>
2521
{{#each fields}}
2622
<tr>
2723
<th scope="row">{{name}}</th>

templates/next/interfaces/foo.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
{{#each imports}}
2-
import { {{type}} } from "{{file}}";
3-
{{/each}}
4-
{{#if imports.length}}
5-
6-
{{/if}}
71
export interface {{{ucf}}} {
82
'@id'?: string;
93
id?: string;

0 commit comments

Comments
 (0)