diff --git a/.eslintignore b/.eslintignore
index be16d13ed64..f729b9fb21d 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,3 +3,4 @@ build
lib
esm
prism.js
+packages/create-react-admin/templates/**
diff --git a/.prettierignore b/.prettierignore
index dd449725e18..54f7990d656 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1 +1,2 @@
*.md
+packages/create-react-admin/templates/**
\ No newline at end of file
diff --git a/packages/create-react-admin/src/ProjectState.ts b/packages/create-react-admin/src/ProjectState.ts
index 8f8d3e57100..9b12a13a8ae 100644
--- a/packages/create-react-admin/src/ProjectState.ts
+++ b/packages/create-react-admin/src/ProjectState.ts
@@ -12,6 +12,7 @@ export type ProjectConfiguration = {
dataProvider: string;
authProvider: string;
resources: string[];
+ messages: string[];
installer: string;
};
@@ -21,5 +22,6 @@ export const InitialProjectConfiguration: ProjectConfiguration = {
dataProvider: '',
authProvider: '',
resources: [],
+ messages: [],
installer: '',
};
diff --git a/packages/create-react-admin/src/SelectInputChoice.tsx b/packages/create-react-admin/src/SelectInputChoice.tsx
index e0def89c17f..282677b0f61 100644
--- a/packages/create-react-admin/src/SelectInputChoice.tsx
+++ b/packages/create-react-admin/src/SelectInputChoice.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Text } from 'ink';
-import { Stack } from './Stack.js';
+import { Stack } from './Stack';
export type ChoiceType = {
label: string;
diff --git a/packages/create-react-admin/src/StepAuthProvider.tsx b/packages/create-react-admin/src/StepAuthProvider.tsx
index cf36bcc19f6..9141bbe071d 100644
--- a/packages/create-react-admin/src/StepAuthProvider.tsx
+++ b/packages/create-react-admin/src/StepAuthProvider.tsx
@@ -1,8 +1,8 @@
import * as React from 'react';
import { Text } from 'ink';
import SelectInput from 'ink-select-input';
-import { ChoiceType, SelectInputChoice } from './SelectInputChoice.js';
-import { Stack } from './Stack.js';
+import { ChoiceType, SelectInputChoice } from './SelectInputChoice';
+import { Stack } from './Stack';
const SupportedAuthProviders: ChoiceType[] = [
{
diff --git a/packages/create-react-admin/src/StepDataProvider.tsx b/packages/create-react-admin/src/StepDataProvider.tsx
index bea91d0888d..0695abff9f8 100644
--- a/packages/create-react-admin/src/StepDataProvider.tsx
+++ b/packages/create-react-admin/src/StepDataProvider.tsx
@@ -1,8 +1,8 @@
import * as React from 'react';
import { Text } from 'ink';
import SelectInput from 'ink-select-input';
-import { ChoiceType, SelectInputChoice } from './SelectInputChoice.js';
-import { Stack } from './Stack.js';
+import { ChoiceType, SelectInputChoice } from './SelectInputChoice';
+import { Stack } from './Stack';
const SupportedDataProviders: ChoiceType[] = [
{
diff --git a/packages/create-react-admin/src/StepGenerate.tsx b/packages/create-react-admin/src/StepGenerate.tsx
new file mode 100644
index 00000000000..faeaf9e4db4
--- /dev/null
+++ b/packages/create-react-admin/src/StepGenerate.tsx
@@ -0,0 +1,20 @@
+import React, { useEffect } from 'react';
+import { Text } from 'ink';
+import { generateProject } from './generateProject';
+import { ProjectConfiguration } from './ProjectState';
+
+export const StepGenerate = ({
+ config,
+ onCompleted,
+}: {
+ config: ProjectConfiguration;
+ onCompleted: (value: any) => void;
+}) => {
+ useEffect(() => {
+ generateProject(config).then(messages => onCompleted({ messages }));
+ // Disabled as we want to run this only once
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return Generating your application...;
+};
diff --git a/packages/create-react-admin/src/StepInstall.tsx b/packages/create-react-admin/src/StepInstall.tsx
index ccef8f500ab..706f71de343 100644
--- a/packages/create-react-admin/src/StepInstall.tsx
+++ b/packages/create-react-admin/src/StepInstall.tsx
@@ -1,8 +1,8 @@
import * as React from 'react';
-import SelectInput from 'ink-select-input';
-import { ChoiceType, SelectInputChoice } from './SelectInputChoice.js';
import { Text } from 'ink';
-import { Stack } from './Stack.js';
+import SelectInput from 'ink-select-input';
+import { ChoiceType, SelectInputChoice } from './SelectInputChoice';
+import { Stack } from './Stack';
const choices: ChoiceType[] = [
{
diff --git a/packages/create-react-admin/src/StepName.tsx b/packages/create-react-admin/src/StepName.tsx
index 89900eee71d..0911e736543 100644
--- a/packages/create-react-admin/src/StepName.tsx
+++ b/packages/create-react-admin/src/StepName.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { useState } from 'react';
import TextInput from 'ink-text-input';
import { Text } from 'ink';
-import { Stack } from './Stack.js';
+import { Stack } from './Stack';
export const StepName = ({
onSubmit,
diff --git a/packages/create-react-admin/src/StepResources.tsx b/packages/create-react-admin/src/StepResources.tsx
index 2dbb8c879f6..0e09a174c2c 100644
--- a/packages/create-react-admin/src/StepResources.tsx
+++ b/packages/create-react-admin/src/StepResources.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { useState } from 'react';
import TextInput from 'ink-text-input';
import { Text } from 'ink';
-import { Stack } from './Stack.js';
+import { Stack } from './Stack';
export const StepResources = ({
onSubmit,
diff --git a/packages/create-react-admin/src/StepRunInstall.tsx b/packages/create-react-admin/src/StepRunInstall.tsx
new file mode 100644
index 00000000000..b51781c783b
--- /dev/null
+++ b/packages/create-react-admin/src/StepRunInstall.tsx
@@ -0,0 +1,28 @@
+import React, { useEffect } from 'react';
+import { Text } from 'ink';
+import { ProjectConfiguration } from './ProjectState';
+import { useInstallDeps } from './useInstallDeps';
+import { useRunFormatter } from './useRunFormatter';
+
+export const StepRunInstall = ({
+ config,
+ onCompleted,
+}: {
+ config: ProjectConfiguration;
+ onCompleted: (value: any) => void;
+}) => {
+ const installDeps = useInstallDeps();
+ const runFormatter = useRunFormatter();
+
+ useEffect(() => {
+ installDeps(config).then(() => {
+ runFormatter(config).then(() => {
+ onCompleted({});
+ });
+ });
+ // Disabled as we want to run this only once
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return Generating your application...;
+};
diff --git a/packages/create-react-admin/src/app.tsx b/packages/create-react-admin/src/app.tsx
index 8e8c917d078..ce422c59e29 100644
--- a/packages/create-react-admin/src/app.tsx
+++ b/packages/create-react-admin/src/app.tsx
@@ -1,16 +1,16 @@
-import React, { useReducer, useRef } from 'react';
+import React, { useReducer } from 'react';
import { Box, Text, Newline } from 'ink';
import {
InitialProjectConfiguration,
ProjectConfiguration,
} from './ProjectState.js';
-import { generateProject } from './generateProject.js';
import { StepDataProvider } from './StepDataProvider.js';
import { StepAuthProvider } from './StepAuthProvider.js';
import { StepResources } from './StepResources.js';
import { StepInstall } from './StepInstall.js';
-import { useInstallDeps } from './useInstallDeps.js';
import { StepName } from './StepName.js';
+import { StepGenerate } from './StepGenerate';
+import { StepRunInstall } from './StepRunInstall';
type Props = {
name: string | undefined;
@@ -61,6 +61,7 @@ const stepReducer = (
case 'generate':
return {
...state,
+ messages: action.value.messages,
step: state.installer ? 'run-install' : 'finish',
};
case 'run-install':
@@ -80,8 +81,7 @@ export default function App({ name = 'my-admin' }: Props) {
name: sanitizedName,
step: sanitizedName === name ? 'data-provider' : 'name',
});
- const helpMessages = useRef([]);
- const installDeps = useInstallDeps();
+
const handleSubmit = (value: any) => {
dispatch({ value });
};
@@ -102,17 +102,10 @@ export default function App({ name = 'my-admin' }: Props) {
return ;
}
if (state.step === 'generate') {
- generateProject(state).then(messages => {
- helpMessages.current = messages;
- dispatch({});
- });
- return Generating your application...;
+ return ;
}
if (state.step === 'run-install') {
- installDeps(state).then(() => {
- dispatch({});
- });
- return Installing dependencies...;
+ return ;
}
return (
<>
@@ -144,7 +137,7 @@ export default function App({ name = 'my-admin' }: Props) {
)}
- {helpMessages.current.map((line, index) => (
+ {state.messages.map((line, index) => (
{line}
))}
diff --git a/packages/create-react-admin/src/generateProject.ts b/packages/create-react-admin/src/generateProject.ts
index 844b797ad50..756e18eb434 100644
--- a/packages/create-react-admin/src/generateProject.ts
+++ b/packages/create-react-admin/src/generateProject.ts
@@ -178,19 +178,28 @@ const BasePackageJson = {
build: 'vite build',
serve: 'vite preview',
'type-check': 'tsc --noEmit',
+ lint: 'eslint --fix --ext .js,.jsx,.ts,.tsx ./src',
+ format: 'prettier --write ./src',
},
dependencies: {
react: '^18.2.0',
- 'react-admin': '^4.9.0',
+ 'react-admin': '^4.11.3',
'react-dom': '^18.2.0',
},
devDependencies: {
+ '@typescript-eslint/parser': '^5.60.1',
+ '@typescript-eslint/eslint-plugin': '^5.60.1',
'@types/node': '^18.16.1',
'@types/react': '^18.0.22',
'@types/react-dom': '^18.0.7',
- '@vitejs/plugin-react': '^2.2.0',
- typescript: '^4.6.4',
- vite: '^3.2.0',
+ '@vitejs/plugin-react': '^4.0.1',
+ eslint: '^8.43.0',
+ 'eslint-config-prettier': '^8.8.0',
+ 'eslint-plugin-react': '^7.32.2',
+ 'eslint-plugin-react-hooks': '^4.6.0',
+ prettier: '^2.8.8',
+ typescript: '^5.1.6',
+ vite: '^4.3.9',
},
};
diff --git a/packages/create-react-admin/src/useRunFormatter.ts b/packages/create-react-admin/src/useRunFormatter.ts
new file mode 100644
index 00000000000..bbf0e1866bc
--- /dev/null
+++ b/packages/create-react-admin/src/useRunFormatter.ts
@@ -0,0 +1,16 @@
+import execa from 'execa';
+import { useStderr } from 'ink';
+import { ProjectConfiguration } from './ProjectState';
+
+export const useRunFormatter = () => {
+ const { stderr } = useStderr();
+
+ return async (state: ProjectConfiguration) => {
+ const command = execa(`${state.installer}`, ['run', 'format'], {
+ cwd: `./${state.name}`,
+ });
+ command.stderr.pipe(stderr);
+
+ await command;
+ };
+};
diff --git a/packages/create-react-admin/templates/common/.eslintrc.js b/packages/create-react-admin/templates/common/.eslintrc.js
new file mode 100644
index 00000000000..ed680282c37
--- /dev/null
+++ b/packages/create-react-admin/templates/common/.eslintrc.js
@@ -0,0 +1,20 @@
+module.exports = {
+ "extends": [
+ "eslint:recommended",
+ "plugin:react/recommended",
+ "plugin:react/jsx-runtime",
+ "plugin:react-hooks/recommended",
+ "prettier"
+ ],
+ "parser": "@typescript-eslint/parser",
+ "plugins": ["@typescript-eslint"],
+ "env": {
+ "browser": true,
+ "es2021": true
+ },
+ "settings": {
+ "react": {
+ "version": "detect"
+ }
+ }
+}
diff --git a/packages/create-react-admin/templates/common/.gitignore b/packages/create-react-admin/templates/common/.gitignore
new file mode 100644
index 00000000000..54f07af58b4
--- /dev/null
+++ b/packages/create-react-admin/templates/common/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
\ No newline at end of file
diff --git a/packages/create-react-admin/templates/common/prettier.config.js b/packages/create-react-admin/templates/common/prettier.config.js
new file mode 100644
index 00000000000..7c6d6c73d3d
--- /dev/null
+++ b/packages/create-react-admin/templates/common/prettier.config.js
@@ -0,0 +1 @@
+module.exports = {}
\ No newline at end of file
diff --git a/packages/create-react-admin/templates/local-auth-provider/authProvider.ts b/packages/create-react-admin/templates/local-auth-provider/authProvider.ts
index cfc6bda8c7b..eb5bc09f40e 100644
--- a/packages/create-react-admin/templates/local-auth-provider/authProvider.ts
+++ b/packages/create-react-admin/templates/local-auth-provider/authProvider.ts
@@ -11,6 +11,7 @@ export const authProvider: AuthProvider = {
);
if (user) {
+ // eslint-disable-next-line no-unused-vars
let { password, ...userToPersist } = user;
localStorage.setItem('user', JSON.stringify(userToPersist));
return Promise.resolve();
diff --git a/packages/create-react-admin/templates/ra-data-fakerest/package.json b/packages/create-react-admin/templates/ra-data-fakerest/package.json
index 46570f19f96..8349ded83c5 100644
--- a/packages/create-react-admin/templates/ra-data-fakerest/package.json
+++ b/packages/create-react-admin/templates/ra-data-fakerest/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "ra-data-fakerest": "^4.9.2"
+ "ra-data-fakerest": "^4.11.3"
}
-}
\ No newline at end of file
+}
diff --git a/packages/create-react-admin/templates/ra-data-json-server/package.json b/packages/create-react-admin/templates/ra-data-json-server/package.json
index 97afe458a83..ec8e8a48ccb 100644
--- a/packages/create-react-admin/templates/ra-data-json-server/package.json
+++ b/packages/create-react-admin/templates/ra-data-json-server/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "ra-data-json-server": "^4.9.2"
+ "ra-data-json-server": "^4.11.3"
}
-}
\ No newline at end of file
+}
diff --git a/packages/create-react-admin/templates/ra-data-simple-rest/package.json b/packages/create-react-admin/templates/ra-data-simple-rest/package.json
index 8cd003bde8e..37ac8e508ef 100644
--- a/packages/create-react-admin/templates/ra-data-simple-rest/package.json
+++ b/packages/create-react-admin/templates/ra-data-simple-rest/package.json
@@ -1,5 +1,5 @@
{
"dependencies": {
- "ra-data-simple-rest": "^4.9.2"
+ "ra-data-simple-rest": "^4.11.3"
}
-}
\ No newline at end of file
+}