-
-
Notifications
You must be signed in to change notification settings - Fork 132
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new version for react native generator
- Loading branch information
Showing
21 changed files
with
1,753 additions
and
868 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
import chalk from "chalk"; | ||
import handlebars from "handlebars"; | ||
import BaseGenerator from "./BaseGenerator.js"; | ||
import hbhComparison from "handlebars-helpers/lib/comparison.js"; | ||
|
||
export default class extends BaseGenerator { | ||
constructor(params) { | ||
super(params); | ||
|
||
handlebars.registerHelper("ifNotResource", function (item, options) { | ||
if (item === null) { | ||
return options.fn(this); | ||
} | ||
return options.inverse(this); | ||
}); | ||
|
||
this.registerTemplates(`react-native-v2/`, [ | ||
"app/(tabs)/foos.tsx", | ||
"app/_layout.tsx.dist", | ||
"lib/hooks.ts", | ||
"lib/store.ts", | ||
"lib/types/ApiResource.ts", | ||
"lib/types/HydraView.ts", | ||
"lib/types/Logs.ts", | ||
"lib/types/foo.ts", | ||
"lib/factory/logFactory.ts", | ||
"lib/slices/fooSlice.ts", | ||
"lib/api/fooApi.ts", | ||
"components/Main.tsx", | ||
"components/Navigation.tsx", | ||
"components/StoreProvider.tsx", | ||
"components/foo/CreateEditModal.tsx", | ||
"components/foo/Form.tsx", | ||
"components/foo/LogsRenderer.tsx", | ||
]); | ||
|
||
handlebars.registerHelper("compare", hbhComparison.compare); | ||
} | ||
|
||
help(resource) { | ||
const titleLc = resource.title.toLowerCase(); | ||
|
||
console.log( | ||
'Code for the "%s" resource type has been generated!', | ||
resource.title | ||
); | ||
|
||
console.log("You must now configure the lib/store.ts"); | ||
console.log( | ||
chalk.green(` | ||
// imports for ${titleLc} | ||
import ${titleLc}Slice from './slices/${titleLc}Slice'; | ||
import { ${titleLc}Api } from './api/${titleLc}Api'; | ||
// reducer for ${titleLc} | ||
reducer: { | ||
... | ||
${titleLc}: ${titleLc}Slice, | ||
[${titleLc}Api.reducerPath]: ${titleLc}Api.reducer, | ||
} | ||
// middleware for ${titleLc} | ||
getDefaultMiddleware().concat(..., ${titleLc}Api.middleware) | ||
`) | ||
); | ||
|
||
console.log( | ||
"You should replace app/_layout.tsx by the generated one and add the following route:" | ||
); | ||
console.log( | ||
chalk.green(` | ||
<Tabs.Screen | ||
name="(tabs)/${titleLc}s" | ||
options={options.tabs.${titleLc}} | ||
/> | ||
tabs: { | ||
... | ||
${titleLc}: { | ||
title: '${titleLc}', | ||
headerShown: false, | ||
tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />, | ||
}, | ||
} | ||
`) | ||
); | ||
} | ||
|
||
generate(api, resource, dir) { | ||
const lc = resource.title.toLowerCase(); | ||
const titleUcFirst = | ||
resource.title.charAt(0).toUpperCase() + resource.title.slice(1); | ||
const fields = this.parseFields(resource); | ||
|
||
const context = { | ||
title: resource.title, | ||
name: resource.name, | ||
lc, | ||
uc: resource.title.toUpperCase(), | ||
fields, | ||
formFields: this.buildFields(fields), | ||
hydraPrefix: this.hydraPrefix, | ||
ucf: titleUcFirst, | ||
}; | ||
|
||
// Create directories | ||
// These directories may already exist | ||
[ | ||
`${dir}/app/(tabs)`, | ||
`${dir}/config`, | ||
`${dir}/components`, | ||
`${dir}/components/${lc}`, | ||
`${dir}/lib`, | ||
`${dir}/lib/api`, | ||
`${dir}/lib/factory`, | ||
`${dir}/lib/slices`, | ||
`${dir}/lib/types`, | ||
].forEach((dir) => this.createDir(dir, false)); | ||
|
||
// static files | ||
[ | ||
"lib/hooks.ts", | ||
"lib/store.ts", | ||
"lib/types/ApiResource.ts", | ||
"lib/types/HydraView.ts", | ||
"lib/types/Logs.ts", | ||
"lib/factory/logFactory.ts", | ||
"components/Main.tsx", | ||
"components/Navigation.tsx", | ||
"components/StoreProvider.tsx", | ||
].forEach((file) => this.createFile(file, `${dir}/${file}`)); | ||
|
||
// templated files ucFirst | ||
["lib/types/%s.ts"].forEach((pattern) => | ||
this.createFileFromPattern(pattern, dir, [titleUcFirst], context) | ||
); | ||
|
||
// templated files lc | ||
[ | ||
"app/(tabs)/%ss.tsx", | ||
"app/_layout.tsx.dist", | ||
"lib/slices/%sSlice.ts", | ||
"lib/api/%sApi.ts", | ||
"components/%s/CreateEditModal.tsx", | ||
"components/%s/Form.tsx", | ||
"components/%s/LogsRenderer.tsx", | ||
].forEach((pattern) => | ||
this.createFileFromPattern(pattern, dir, [lc], context) | ||
); | ||
|
||
this.createEntrypoint(api.entrypoint, `${dir}/config/entrypoint.js`); | ||
} | ||
|
||
getDescription(field) { | ||
return field.description ? field.description.replace(/"/g, "'") : ""; | ||
} | ||
|
||
parseFields(resource) { | ||
const fields = [ | ||
...resource.writableFields, | ||
...resource.readableFields, | ||
].reduce((list, field) => { | ||
if (list[field.name]) { | ||
return list; | ||
} | ||
|
||
const isReferences = Boolean( | ||
field.reference && field.maxCardinality !== 1 | ||
); | ||
const isEmbeddeds = Boolean(field.embedded && field.maxCardinality !== 1); | ||
|
||
return { | ||
...list, | ||
[field.name]: { | ||
...field, | ||
type: this.getType(field), | ||
description: this.getDescription(field), | ||
readonly: false, | ||
isReferences, | ||
isEmbeddeds, | ||
isRelations: isEmbeddeds || isReferences, | ||
}, | ||
}; | ||
}, {}); | ||
|
||
return Object.values(fields); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { Api, Resource, Field } from "@api-platform/api-doc-parser"; | ||
import path from "path"; | ||
import { fileURLToPath } from "url"; | ||
import fs from "fs"; | ||
import tmp from "tmp"; | ||
import ReactNativeGeneratorV2 from "./ReactNativeGeneratorV2.js"; | ||
|
||
const dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
|
||
test("Generate a React Native V2 app", () => { | ||
const generator = new ReactNativeGeneratorV2({ | ||
hydraPrefix: "hydra:", | ||
templateDirectory: `${dirname}/../../templates`, | ||
}); | ||
const tmpobj = tmp.dirSync({ unsafeCleanup: true }); | ||
|
||
const fields = [ | ||
new Field("bar", { | ||
id: "http://schema.org/url", | ||
range: "http://www.w3.org/2001/XMLSchema#string", | ||
reference: null, | ||
required: true, | ||
description: "An URL", | ||
}), | ||
]; | ||
const resource = new Resource("abc", "http://example.com/foos", { | ||
id: "abc", | ||
title: "abc", | ||
readableFields: fields, | ||
writableFields: fields, | ||
}); | ||
const api = new Api("http://example.com", { | ||
entrypoint: "http://example.com:8080", | ||
title: "My API", | ||
resources: [resource], | ||
}); | ||
generator.generate(api, resource, tmpobj.name); | ||
|
||
[ | ||
"/lib/hooks.ts", | ||
"/lib/store.ts", | ||
"/lib/types/ApiResource.ts", | ||
"/lib/types/HydraView.ts", | ||
"/lib/types/Logs.ts", | ||
"/lib/factory/logFactory.ts", | ||
"/components/Main.tsx", | ||
"/components/Navigation.tsx", | ||
"/components/StoreProvider.tsx", | ||
"/app/_layout.tsx.dist", | ||
|
||
"/app/(tabs)/abcs.tsx", | ||
"/lib/slices/abcSlice.ts", | ||
"/lib/api/abcApi.ts", | ||
"/components/abc/CreateEditModal.tsx", | ||
"/components/abc/Form.tsx", | ||
"/components/abc/LogsRenderer.tsx", | ||
].forEach((file) => expect(fs.existsSync(tmpobj.name + file)).toBe(true)); | ||
|
||
tmpobj.removeCallback(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import Main from "@/components/Main"; | ||
import Navigation from "@/components/Navigation"; | ||
import CreateEditModal from "@/components/{{{lc}}}/CreateEditModal"; | ||
import LogsRenderer from "@/components/{{{lc}}}/LogsRenderer"; | ||
import { useLazyGetAllQuery } from "@/lib/api/{{{lc}}}Api"; | ||
import { useAppDispatch, useAppSelector } from "@/lib/hooks"; | ||
import { setCurrentData, setData, setModalIsEdit, setModalIsVisible, setPage, setView } from "@/lib/slices/{{{lc}}}Slice"; | ||
import {{{ucf}}} from "@/lib/types/{{{ucf}}}"; | ||
import { useLocalSearchParams } from "expo-router"; | ||
import { useEffect } from "react"; | ||
import { Pressable, ScrollView, Text, View } from "react-native"; | ||
|
||
export default function {{{ucf}}}s() { | ||
const datas = useAppSelector(state => state.{{lc}}.data); | ||
const view = useAppSelector(state => state.{{{lc}}}.view); | ||
const { page = '1' } = useLocalSearchParams<{ page: string }>(); | ||
|
||
const dispatch = useAppDispatch(); | ||
const [getAll] = useLazyGetAllQuery(); | ||
|
||
const toggleEditModal = (data: {{{ucf}}}) => { | ||
dispatch(setCurrentData(data)); | ||
dispatch(setModalIsVisible(true)); | ||
dispatch(setModalIsEdit(true)); | ||
}; | ||
|
||
const toggleCreateModal = () => { | ||
dispatch(setModalIsVisible(true)); | ||
dispatch(setModalIsEdit(false)); | ||
} | ||
|
||
useEffect(() => { | ||
const intPage = parseInt(page); | ||
if (intPage < 0) return; | ||
dispatch(setPage(intPage)); | ||
getAll(intPage) | ||
.unwrap() | ||
.then(fulfilled => { | ||
dispatch(setView(fulfilled["hydra:view"])); | ||
dispatch(setData(fulfilled["hydra:member"])); | ||
}) | ||
}, [page]); | ||
|
||
return ( | ||
<Main> | ||
<View className="py-3 flex flex-row items-center justify-between"> | ||
<Text className="text-3xl">{{{ucf}}}s List</Text> | ||
<Pressable onPress={() => toggleCreateModal()}> | ||
<Text className="bg-cyan-500 cursor-pointer text-white text-sm font-bold py-2 px-4 rounded">Create</Text> | ||
</Pressable> | ||
</View> | ||
<ScrollView> | ||
<LogsRenderer /> | ||
<View> | ||
{ | ||
datas.map(data => ( | ||
<Pressable onPress={() => toggleEditModal(data)} key={data["@id"]}> | ||
<View className="flex flex-column my-2 block max-w p-6 bg-white border border-gray-300 rounded shadow"> | ||
<Text>ID: {data['@id']}</Text> | ||
{{#each fields}} | ||
<Text>{{{name}}}: {data["{{{name}}}"]}</Text> | ||
{{/each}} | ||
</View> | ||
</Pressable> | ||
)) | ||
} | ||
</View> | ||
<CreateEditModal /> | ||
</ScrollView> | ||
<Navigation view={view} /> | ||
</Main > | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import React from 'react'; | ||
import { FontAwesome } from "@expo/vector-icons"; | ||
import "../global.css"; | ||
import { Tabs } from "expo-router"; | ||
import StoreProvider from '@/components/StoreProvider'; | ||
|
||
function TabBarIcon(props: { | ||
name: React.ComponentProps<typeof FontAwesome>['name']; | ||
color: string; | ||
}) { | ||
return <FontAwesome size={28} style={iconMargin} {...props} />; | ||
} | ||
const iconMargin = { marginBottom: -3 } | ||
|
||
export default function Layout() { | ||
return ( | ||
<StoreProvider> | ||
<Tabs screenOptions={options.tabsContainer}> | ||
<Tabs.Screen | ||
name="index" | ||
options={options.tabs.home} | ||
/> | ||
</Tabs> | ||
</StoreProvider> | ||
) | ||
} | ||
|
||
const options = { | ||
tabsContainer: { | ||
headerShown: false, | ||
tabBarShowLabel: false, | ||
}, | ||
tabs: { | ||
home: { | ||
title: 'Accueil', | ||
tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />, | ||
}, | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { View } from "react-native"; | ||
|
||
export default function Main({ children }) { | ||
return ( | ||
<View className="flex flex-1 py-16" style={styles.container}> | ||
{children} | ||
</View> | ||
) | ||
} | ||
|
||
const styles = { | ||
container: { | ||
position: 'relative', | ||
marginHorizontal: '3%', | ||
} | ||
} |
Oops, something went wrong.