Skip to content

Commit 8501453

Browse files
committed
conflict resolution
1 parent 309e021 commit 8501453

File tree

26 files changed

+546
-17
lines changed

26 files changed

+546
-17
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"lint": "eslint src",
4444
"build": "babel src -d lib --ignore '*.test.js'",
4545
"watch": "babel --watch src -d lib --ignore '*.test.js'",
46-
"test-gen": "rm -rf ./tmp && npm run build && ./lib/index.js https://demo.api-platform.com ./tmp",
46+
"test-gen": "rm -rf ./tmp && npm run build && ./lib/index.js https://demo.api-platform.com ./tmp/react && ./lib/index.js https://demo.api-platform.com ./tmp/react-native -g react-native",
4747
"test-gen-env": "rm -rf ./tmp && npm run build && API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=https://demo.api-platform.com API_PLATFORM_CLIENT_GENERATOR_OUTPUT=./tmp ./lib/index.js"
4848
},
4949
"bin": {

src/generators.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ReactCrudGenerator from './generators/ReactCrudGenerator';
2+
import ReactNativeCrudGenerator from './generators/ReactNativeCrudGenerator';
23
import TypescriptInterfaceGenerator from './generators/TypescriptInterfaceGenerator';
34

45
function wrap (cl) {
@@ -9,6 +10,8 @@ function generators (generator = 'react') {
910
switch (generator) {
1011
case 'react':
1112
return wrap(ReactCrudGenerator);
13+
case 'react-native':
14+
return wrap(ReactNativeCrudGenerator);
1215
case 'typescript':
1316
return wrap(TypescriptInterfaceGenerator);
1417
}

src/generators/ReactCrudGenerator.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ export default class ReactCrudGenerator {
88
templates = {};
99

1010
constructor({hydraPrefix, templateDirectory}) {
11+
const templatePathCommon = `${templateDirectory}/react-common/`;
1112
const templatePath = `${templateDirectory}/react/`;
1213

1314
this.hydraPrefix = hydraPrefix;
1415

1516
// actions
16-
this.registerTemplate(templatePath, 'actions/foo/create.js');
17-
this.registerTemplate(templatePath, 'actions/foo/delete.js');
18-
this.registerTemplate(templatePath, 'actions/foo/list.js');
19-
this.registerTemplate(templatePath, 'actions/foo/update.js');
20-
this.registerTemplate(templatePath, 'actions/foo/show.js');
17+
this.registerTemplate(templatePathCommon, 'actions/foo/create.js');
18+
this.registerTemplate(templatePathCommon, 'actions/foo/delete.js');
19+
this.registerTemplate(templatePathCommon, 'actions/foo/list.js');
20+
this.registerTemplate(templatePathCommon, 'actions/foo/update.js');
21+
this.registerTemplate(templatePathCommon, 'actions/foo/show.js');
2122

2223
// api
23-
this.registerTemplate(templatePath, 'api/fooFetch.js');
24+
this.registerTemplate(templatePathCommon, 'api/fooFetch.js');
2425

2526
// components
2627
this.registerTemplate(templatePath, 'components/foo/Create.js');
@@ -31,25 +32,25 @@ export default class ReactCrudGenerator {
3132
this.registerTemplate(templatePath, 'components/foo/Show.js');
3233

3334
// reducers
34-
this.registerTemplate(templatePath, 'reducers/foo/create.js');
35-
this.registerTemplate(templatePath, 'reducers//foo/delete.js');
36-
this.registerTemplate(templatePath, 'reducers/foo/index.js');
37-
this.registerTemplate(templatePath, 'reducers/foo/list.js');
38-
this.registerTemplate(templatePath, 'reducers/foo/update.js');
39-
this.registerTemplate(templatePath, 'reducers/foo/show.js');
35+
this.registerTemplate(templatePathCommon, 'reducers/foo/create.js');
36+
this.registerTemplate(templatePathCommon, 'reducers//foo/delete.js');
37+
this.registerTemplate(templatePathCommon, 'reducers/foo/index.js');
38+
this.registerTemplate(templatePathCommon, 'reducers/foo/list.js');
39+
this.registerTemplate(templatePathCommon, 'reducers/foo/update.js');
40+
this.registerTemplate(templatePathCommon, 'reducers/foo/show.js');
4041

4142
// routes
4243
this.registerTemplate(templatePath, 'routes/foo.js');
4344

4445
// entrypoint
45-
this.registerTemplate(templatePath, 'api/_entrypoint.js');
46+
this.registerTemplate(templatePathCommon, 'api/_entrypoint.js');
4647

4748
// utils
4849
this.registerTemplate(templatePath, 'utils/helpers.js');
4950
}
5051

5152
registerTemplate(templatePath, path) {
52-
this.templates[path] = handlebars.compile(fs.readFileSync(templatePath+path).toString());
53+
this.templates[path] = handlebars.compile(fs.readFileSync(templatePath + path).toString());
5354
}
5455

5556
help(resource) {
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import mkdirp from 'mkdirp';
2+
import handlebars from 'handlebars';
3+
import fs from 'fs';
4+
import urlapi from 'url';
5+
import chalk from 'chalk';
6+
7+
export default class ReactNativeCrudGenerator {
8+
templates = {};
9+
10+
constructor({hydraPrefix, templateDirectory}) {
11+
const templatePathCommon = `${templateDirectory}/react-common/`;
12+
const templatePath = `${templateDirectory}/react/`;
13+
14+
this.hydraPrefix = hydraPrefix;
15+
16+
// actions
17+
this.registerTemplate(templatePathCommon, 'actions/foo/create.js');
18+
this.registerTemplate(templatePathCommon, 'actions/foo/delete.js');
19+
this.registerTemplate(templatePathCommon, 'actions/foo/list.js');
20+
this.registerTemplate(templatePathCommon, 'actions/foo/update.js');
21+
this.registerTemplate(templatePathCommon, 'actions/foo/show.js');
22+
23+
// api
24+
this.registerTemplate(templatePathCommon, 'api/fooFetch.js');
25+
26+
// components
27+
this.registerTemplate(templatePath, 'components/foo/Create.js');
28+
this.registerTemplate(templatePath, 'components/foo/Form.js');
29+
this.registerTemplate(templatePath, 'components/foo/index.js');
30+
this.registerTemplate(templatePath, 'components/foo/List.js');
31+
this.registerTemplate(templatePath, 'components/foo/Update.js');
32+
this.registerTemplate(templatePath, 'components/foo/Show.js');
33+
34+
// reducers
35+
this.registerTemplate(templatePathCommon, 'reducers/foo/create.js');
36+
this.registerTemplate(templatePathCommon, 'reducers//foo/delete.js');
37+
this.registerTemplate(templatePathCommon, 'reducers/foo/index.js');
38+
this.registerTemplate(templatePathCommon, 'reducers/foo/list.js');
39+
this.registerTemplate(templatePathCommon, 'reducers/foo/update.js');
40+
this.registerTemplate(templatePathCommon, 'reducers/foo/show.js');
41+
42+
// entrypoint
43+
this.registerTemplate(templatePathCommon, 'api/_entrypoint.js');
44+
}
45+
46+
registerTemplate(templatePath, path) {
47+
this.templates[path] = handlebars.compile(fs.readFileSync(templatePath+path).toString());
48+
}
49+
50+
help(resource) {
51+
const titleLc = resource.title.toLowerCase()
52+
53+
console.log('Code for the "%s" resource type has been generated!', resource.title);
54+
console.log('Paste the following definitions in your application configuration:');
55+
console.log(chalk.green(`
56+
// import reducers
57+
import ${titleLc} from './reducers/${titleLc}/';
58+
<<<<<<< HEAD
59+
// Add the reducer
60+
combineReducers(${titleLc},{/* ... */}),
61+
=======
62+
63+
// Add the reducer
64+
combineReducers(${titleLc},{/* ... */}),
65+
66+
>>>>>>> 3d43b1f377ef84d790d6022c6f61df5301c28246
67+
`));
68+
}
69+
70+
generate(api, resource, dir) {
71+
const lc = resource.title.toLowerCase();
72+
const titleUcFirst = resource.title.charAt(0).toUpperCase() + resource.title.slice(1);
73+
74+
const context = {
75+
title: resource.title,
76+
name: resource.name,
77+
lc,
78+
uc: resource.title.toUpperCase(),
79+
fields: resource.readableFields,
80+
formFields: this.buildFields(resource.writableFields),
81+
hydraPrefix: this.hydraPrefix,
82+
titleUcFirst
83+
};
84+
85+
86+
// Create directories
87+
// These directories may already exist
88+
mkdirp.sync(`${dir}/api`);
89+
90+
this.createDir(`${dir}/actions/${lc}`);
91+
this.createDir(`${dir}/components/${lc}`);
92+
this.createDir(`${dir}/reducers/${lc}`);
93+
94+
// actions
95+
this.createFile('actions/foo/create.js', `${dir}/actions/${lc}/create.js`, context);
96+
this.createFile('actions/foo/delete.js', `${dir}/actions/${lc}/delete.js`, context);
97+
this.createFile('actions/foo/list.js', `${dir}/actions/${lc}/list.js`, context);
98+
this.createFile('actions/foo/update.js', `${dir}/actions/${lc}/update.js`, context);
99+
this.createFile('actions/foo/show.js', `${dir}/actions/${lc}/show.js`, context);
100+
101+
// api
102+
this.createFile('api/fooFetch.js', `${dir}/api/${lc}Fetch.js`, context);
103+
104+
// components
105+
this.createFile('components/foo/Create.js', `${dir}/components/${lc}/Create.js`, context);
106+
this.createFile('components/foo/Form.js', `${dir}/components/${lc}/Form.js`, context);
107+
this.createFile('components/foo/index.js', `${dir}/components/${lc}/index.js`, context);
108+
this.createFile('components/foo/List.js', `${dir}/components/${lc}/List.js`, context);
109+
this.createFile('components/foo/Update.js', `${dir}/components/${lc}/Update.js`, context);
110+
this.createFile('components/foo/Show.js', `${dir}/components/${lc}/Show.js`, context);
111+
112+
// reducers
113+
this.createFile('reducers/foo/create.js', `${dir}/reducers/${lc}/create.js`, context);
114+
this.createFile('reducers//foo/delete.js', `${dir}/reducers/${lc}/delete.js`, context);
115+
this.createFile('reducers/foo/index.js', `${dir}/reducers/${lc}/index.js`, context);
116+
this.createFile('reducers/foo/list.js', `${dir}/reducers/${lc}/list.js`, context);
117+
this.createFile('reducers/foo/update.js', `${dir}/reducers/${lc}/update.js`, context);
118+
this.createFile('reducers/foo/show.js', `${dir}/reducers/${lc}/show.js`, context);
119+
}
120+
121+
entrypoint(apiEntry, dir) {
122+
const url = urlapi.parse(apiEntry);
123+
const {protocol, host, port, pathname} = url;
124+
const hostUrl = `${protocol}//${host}${port ? `:${port}` : ''}`;
125+
126+
const context = {
127+
host: hostUrl,
128+
path: pathname
129+
}
130+
131+
this.createFile('api/_entrypoint.js', `${dir}/api/_entrypoint.js`, context);
132+
}
133+
134+
getInputTypeFromField(field) {
135+
switch (field.id) {
136+
case 'http://schema.org/email':
137+
return {type: 'email'};
138+
139+
case 'http://schema.org/url':
140+
return {type: 'url'};
141+
}
142+
143+
switch (field.range) {
144+
case 'http://www.w3.org/2001/XMLSchema#integer':
145+
return {type: 'number'};
146+
147+
case 'http://www.w3.org/2001/XMLSchema#decimal':
148+
return {type: 'number', step: '0.1'};
149+
150+
case 'http://www.w3.org/2001/XMLSchema#boolean':
151+
return {type: 'checkbox'};
152+
153+
case 'http://www.w3.org/2001/XMLSchema#date':
154+
return {type: 'date'};
155+
156+
case 'http://www.w3.org/2001/XMLSchema#time':
157+
return {type: 'time'};
158+
159+
default:
160+
return {type: 'text'};
161+
}
162+
}
163+
164+
buildFields(apiFields) {
165+
let fields = [];
166+
for (let apiField of apiFields) {
167+
let field = this.getInputTypeFromField(apiField);
168+
field.required = apiField.required;
169+
field.name = apiField.name;
170+
field.description = apiField.description.replace(/"/g, "'"); // fix for Form placeholder description
171+
172+
fields.push(field)
173+
}
174+
175+
return fields;
176+
}
177+
178+
createDir(dir) {
179+
if (fs.existsSync(dir)) throw new Error(`The directory "${dir}" already exists`);
180+
mkdirp.sync(dir);
181+
}
182+
183+
createFile(template, dest, context) {
184+
fs.writeFileSync(dest, this.templates[template](context));
185+
}
186+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import Api from 'api-doc-parser/lib/Api';
2+
import Resource from 'api-doc-parser/lib/Resource';
3+
import Field from 'api-doc-parser/lib/Field';
4+
import fs from 'fs';
5+
import tmp from 'tmp';
6+
import ReactNativeCrudGenerator from './ReactNativeCrudGenerator';
7+
8+
9+
test('Generate a React app', () => {
10+
const generator = new ReactNativeCrudGenerator({hydraPrefix: 'hydra:', templateDirectory: `${__dirname}/../../templates`});
11+
const tmpobj = tmp.dirSync({unsafeCleanup: true});
12+
13+
const fields = [new Field('bar', {
14+
id: 'http://schema.org/url',
15+
range: 'http://www.w3.org/2001/XMLSchema#string',
16+
reference: null,
17+
required: true,
18+
description: 'An URL'
19+
})];
20+
const resource = new Resource('abc', 'http://example.com/foos', {
21+
id: 'foo',
22+
title: 'Foo',
23+
readableFields: fields,
24+
writableFields: fields
25+
});
26+
const api = new Api('http://example.com', {
27+
title: 'My API',
28+
resources: [resource]
29+
});
30+
generator.generate(api, resource, tmpobj.name);
31+
32+
expect(fs.existsSync(tmpobj.name+'/actions/abc/create.js'), true);
33+
expect(fs.existsSync(tmpobj.name+'/actions/abc/delete.js'), true);
34+
expect(fs.existsSync(tmpobj.name+'/actions/abc/list.js'), true);
35+
expect(fs.existsSync(tmpobj.name+'/actions/abc/update.js'), true);
36+
37+
expect(fs.existsSync(tmpobj.name+'/api/abcFetch.js'), true);
38+
39+
expect(fs.existsSync(tmpobj.name+'/components/abc/Create.js'), true);
40+
expect(fs.existsSync(tmpobj.name+'/components/abc/Form.js'), true);
41+
expect(fs.existsSync(tmpobj.name+'/components/abc/List.js'), true);
42+
expect(fs.existsSync(tmpobj.name+'/components/abc/Update.js'), true);
43+
44+
expect(fs.existsSync(tmpobj.name+'/reducers/abc/create.js'), true);
45+
expect(fs.existsSync(tmpobj.name+'/reducers/abc/delete.js'), true);
46+
expect(fs.existsSync(tmpobj.name+'/reducers/abc/index.js'), true);
47+
expect(fs.existsSync(tmpobj.name+'/reducers/abc/list.js'), true);
48+
expect(fs.existsSync(tmpobj.name+'/reducers/abc/update.js'), true);
49+
50+
tmpobj.removeCallback();
51+
});

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ program
1212
.usage('entrypoint outputDirectory')
1313
.option('-r, --resource [resourceName]', 'Generate CRUD for the given resource')
1414
.option('-p, --hydra-prefix [hydraPrefix]', 'The hydra prefix used by the API', 'hydra:')
15-
.option('-g, --generator [generator]', 'The generator to use, one of "react", "angular" etc.', 'react')
15+
.option('-g, --generator [generator]', 'The generator to use, one of "react", "react-native", "angular", etc.', 'react')
1616
.option('-t, --template-directory [templateDirectory]', 'The templates directory base to use. Final directory will be ${templateDirectory}/${generator}', `${__dirname}/../templates/`)
1717
.parse(process.argv);
1818

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React, {Component} from 'react';
2+
import {ScrollView, Text} from 'react-native';
3+
import {connect} from 'react-redux';
4+
import PropTypes from 'prop-types';
5+
import Form from './Form';
6+
import { create, loading, error } from '../../actions/{{{ lc }}}/create';
7+
8+
class Create extends Component {
9+
static propTypes = {
10+
error: PropTypes.string,
11+
loading: PropTypes.bool.isRequired,
12+
created: PropTypes.object,
13+
create: PropTypes.func.isRequired,
14+
reset: PropTypes.func.isRequired,
15+
};
16+
17+
componentWillUnmount() {
18+
this.props.reset();
19+
}
20+
21+
render() {
22+
return (
23+
<ScrollView>
24+
<Text>Create component {{lc}}</Text>
25+
</ScrollView>
26+
);
27+
}
28+
}
29+
30+
const mapStateToProps = (state) => {
31+
return {
32+
created: state.{{{ lc }}}.create.created,
33+
error: state.{{{ lc }}}.create.error,
34+
loading: state.{{{ lc }}}.create.loading,
35+
};
36+
};
37+
38+
const mapDispatchToProps = (dispatch) => {
39+
return {
40+
create: values => dispatch(create(values)),
41+
reset: () => {
42+
dispatch(loading(false));
43+
dispatch(error(null));
44+
},
45+
};
46+
};
47+
48+
export default connect(mapStateToProps, mapDispatchToProps)(Create);

0 commit comments

Comments
 (0)