create-maedow-arch-app 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/package.json +1 -1
- package/templates/base/scripts/scaffold-domain.mjs +40 -40
- package/templates/base/src/app/page.tsx +19 -19
- package/templates/base/tsconfig.json +27 -28
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# create-maedow-arch-app
|
|
2
2
|
|
|
3
|
-
CLI de scaffolding de [Maedow Arch](https://github.com/maedow-arch/maedow-arch-docs)
|
|
3
|
+
CLI de scaffolding de [Maedow Arch](https://github.com/maedow-arch/maedow-arch-docs), un standard d'architecture logicielle modulaire, découplé et agnostique de l'infrastructure, pensé pour TypeScript, React et Next.js.
|
|
4
4
|
|
|
5
5
|
## Démarrage
|
|
6
6
|
|
|
@@ -16,33 +16,33 @@ npm run dev
|
|
|
16
16
|
```
|
|
17
17
|
mon-projet/
|
|
18
18
|
├── src/
|
|
19
|
-
│ ├── app/ # Routes et orchestration
|
|
19
|
+
│ ├── app/ # Routes et orchestration. Peut tout importer.
|
|
20
20
|
│ ├── features/ # Écrans et logique de vue
|
|
21
21
|
│ │ └── _shared/ # Composants métier transverses
|
|
22
|
-
│ ├── core/ # Domaine métier
|
|
22
|
+
│ ├── core/ # Domaine métier, sans aucune dépendance UI
|
|
23
23
|
│ │ └── common/
|
|
24
|
-
│ │ └── result.ts # Result Pattern
|
|
24
|
+
│ │ └── result.ts # Result Pattern et ses helpers unwrapOr, mapResult, match
|
|
25
25
|
│ ├── components/ui/ # Présentationnel pur
|
|
26
26
|
│ ├── lib/ # Utilitaires sans dépendance
|
|
27
|
-
│ └── tests/ # unit
|
|
27
|
+
│ └── tests/ # unit, integration, e2e
|
|
28
28
|
├── scripts/ # Générateurs de domaine et de feature
|
|
29
29
|
├── eslint.config.mjs # Frontières architecturales appliquées au lint
|
|
30
|
-
├── tsconfig.json # TypeScript strict
|
|
30
|
+
├── tsconfig.json # TypeScript strict : noUncheckedIndexedAccess, exactOptionalPropertyTypes
|
|
31
31
|
└── vitest.config.ts
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Générateurs
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npm run generate:domain billing # src/core/billing/
|
|
38
|
-
npm run generate:feature checkout # src/features/checkout/
|
|
37
|
+
npm run generate:domain billing # src/core/billing/ : types, validation Zod, service
|
|
38
|
+
npm run generate:feature checkout # src/features/checkout/ : Screen, hook, types, test
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Le domaine généré applique la **Règle de Lazy Abstraction** :
|
|
41
|
+
Le domaine généré applique la **Règle de Lazy Abstraction** : il accède directement à la donnée, sans `contract.ts` ni adapters, tant qu'une deuxième implémentation réelle n'est pas nécessaire.
|
|
42
42
|
|
|
43
43
|
## Frontières architecturales
|
|
44
44
|
|
|
45
|
-
Le flux de dépendance est unidirectionnel
|
|
45
|
+
Le flux de dépendance est unidirectionnel, `app → features → core → lib`, et vérifié au lint :
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
npm run lint
|
|
@@ -56,7 +56,7 @@ Les règles vivent dans [`eslint-config-maedow-arch`](https://www.npmjs.com/pack
|
|
|
56
56
|
|
|
57
57
|
## Documentation
|
|
58
58
|
|
|
59
|
-
Le corpus complet
|
|
59
|
+
Le corpus complet couvre les quatre couches, la typologie des modèles, le Result Pattern, les conventions et les modes Light et Full. Il est publié sur [maedow-arch-docs.vercel.app](https://maedow-arch-docs.vercel.app).
|
|
60
60
|
|
|
61
61
|
## Licence
|
|
62
62
|
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
const name = process.argv[2];
|
|
6
|
-
if (!name) {
|
|
7
|
-
console.error("Usage: npm run generate:domain <nom>");
|
|
8
|
-
process.exit(1);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const pascal = name.charAt(0).toUpperCase() + name.slice(1);
|
|
12
|
-
const dir = join("src", "core", name);
|
|
13
|
-
|
|
14
|
-
if (existsSync(dir)) {
|
|
15
|
-
console.error(`Le domaine "${name}" existe déjà dans ${dir}`);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
mkdirSync(dir, { recursive: true });
|
|
20
|
-
|
|
21
|
-
writeFileSync(
|
|
22
|
-
join(dir, "types.ts"),
|
|
23
|
-
`// Entité métier du domaine "${name}"\n\nexport interface ${pascal} {\n id: string;\n // TODO: champs métier\n}\n`
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
writeFileSync(
|
|
27
|
-
join(dir, "validation.ts"),
|
|
28
|
-
`import { z } from "zod";\n\nexport const Create${pascal}Schema = z.object({\n // TODO: champs à valider\n});\n\nexport type Create${pascal}DTO = z.infer<typeof Create${pascal}Schema>;\n`
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
writeFileSync(
|
|
32
|
-
join(dir, "service.ts"),
|
|
33
|
-
`import type { Result } from "../common/result";\nimport type { ${pascal} } from "./types";\nimport type { Create${pascal}DTO } from "./validation";\n\n// ⚠️ Règle de Lazy Abstraction : n'introduis un contract.ts + adapters\n// que lorsqu'une deuxième implémentation réelle est nécessaire.\n// Tant qu'un seul fournisseur de données existe, accède-y directement ici.\n\nexport async function create${pascal}(input: Create${pascal}DTO): Promise<Result<${pascal}>> {\n // TODO: logique métier\n return { ok: false, error: "not_implemented" };\n}\n`
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
console.log(`✅ Domaine "${name}" généré dans ${dir}/`);
|
|
37
|
-
console.log(` - types.ts`);
|
|
38
|
-
console.log(` - validation.ts`);
|
|
39
|
-
console.log(` - service.ts (accès direct
|
|
40
|
-
console.log(` Rappel : n'ajoute contract.ts + repository.ts que si un 2ème fournisseur devient réel.`);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const name = process.argv[2];
|
|
6
|
+
if (!name) {
|
|
7
|
+
console.error("Usage: npm run generate:domain <nom>");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const pascal = name.charAt(0).toUpperCase() + name.slice(1);
|
|
12
|
+
const dir = join("src", "core", name);
|
|
13
|
+
|
|
14
|
+
if (existsSync(dir)) {
|
|
15
|
+
console.error(`Le domaine "${name}" existe déjà dans ${dir}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
|
|
21
|
+
writeFileSync(
|
|
22
|
+
join(dir, "types.ts"),
|
|
23
|
+
`// Entité métier du domaine "${name}"\n\nexport interface ${pascal} {\n id: string;\n // TODO: champs métier\n}\n`
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
writeFileSync(
|
|
27
|
+
join(dir, "validation.ts"),
|
|
28
|
+
`import { z } from "zod";\n\nexport const Create${pascal}Schema = z.object({\n // TODO: champs à valider\n});\n\nexport type Create${pascal}DTO = z.infer<typeof Create${pascal}Schema>;\n`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
writeFileSync(
|
|
32
|
+
join(dir, "service.ts"),
|
|
33
|
+
`import type { Result } from "../common/result";\nimport type { ${pascal} } from "./types";\nimport type { Create${pascal}DTO } from "./validation";\n\n// ⚠️ Règle de Lazy Abstraction : n'introduis un contract.ts + adapters\n// que lorsqu'une deuxième implémentation réelle est nécessaire.\n// Tant qu'un seul fournisseur de données existe, accède-y directement ici.\n\nexport async function create${pascal}(input: Create${pascal}DTO): Promise<Result<${pascal}>> {\n // TODO: logique métier\n return { ok: false, error: "not_implemented" };\n}\n`
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
console.log(`✅ Domaine "${name}" généré dans ${dir}/`);
|
|
37
|
+
console.log(` - types.ts`);
|
|
38
|
+
console.log(` - validation.ts`);
|
|
39
|
+
console.log(` - service.ts (accès direct, voir la Règle de Lazy Abstraction)`);
|
|
40
|
+
console.log(` Rappel : n'ajoute contract.ts + repository.ts que si un 2ème fournisseur devient réel.`);
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
export default function HomePage() {
|
|
2
|
-
return (
|
|
3
|
-
<main style={{ fontFamily: "system-ui, sans-serif", padding: "3rem", lineHeight: 1.6 }}>
|
|
4
|
-
<h1>__PROJECT_NAME__</h1>
|
|
5
|
-
<p>Projet généré avec Maedow Arch.</p>
|
|
6
|
-
<ol>
|
|
7
|
-
<li>
|
|
8
|
-
<code>npm run generate:domain <nom></code>
|
|
9
|
-
</li>
|
|
10
|
-
<li>
|
|
11
|
-
<code>npm run generate:feature <nom></code>
|
|
12
|
-
</li>
|
|
13
|
-
<li>
|
|
14
|
-
<code>npm run lint</code>
|
|
15
|
-
</li>
|
|
16
|
-
</ol>
|
|
17
|
-
</main>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
1
|
+
export default function HomePage() {
|
|
2
|
+
return (
|
|
3
|
+
<main style={{ fontFamily: "system-ui, sans-serif", padding: "3rem", lineHeight: 1.6 }}>
|
|
4
|
+
<h1>__PROJECT_NAME__</h1>
|
|
5
|
+
<p>Projet généré avec Maedow Arch.</p>
|
|
6
|
+
<ol>
|
|
7
|
+
<li>
|
|
8
|
+
<code>npm run generate:domain <nom></code> crée un domaine métier dans <code>src/core/</code>
|
|
9
|
+
</li>
|
|
10
|
+
<li>
|
|
11
|
+
<code>npm run generate:feature <nom></code> crée une feature dans <code>src/features/</code>
|
|
12
|
+
</li>
|
|
13
|
+
<li>
|
|
14
|
+
<code>npm run lint</code> vérifie les frontières architecturales
|
|
15
|
+
</li>
|
|
16
|
+
</ol>
|
|
17
|
+
</main>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -1,28 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"strict": true,
|
|
4
|
-
"noUncheckedIndexedAccess": true,
|
|
5
|
-
"noImplicitOverride": true,
|
|
6
|
-
"noFallthroughCasesInSwitch": true,
|
|
7
|
-
"exactOptionalPropertyTypes": true,
|
|
8
|
-
"target": "ES2022",
|
|
9
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
10
|
-
"module": "ESNext",
|
|
11
|
-
"moduleResolution": "Bundler",
|
|
12
|
-
"jsx": "preserve",
|
|
13
|
-
"allowJs": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"esModuleInterop": true,
|
|
16
|
-
"resolveJsonModule": true,
|
|
17
|
-
"isolatedModules": true,
|
|
18
|
-
"noEmit": true,
|
|
19
|
-
"incremental": true,
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"strict": true,
|
|
4
|
+
"noUncheckedIndexedAccess": true,
|
|
5
|
+
"noImplicitOverride": true,
|
|
6
|
+
"noFallthroughCasesInSwitch": true,
|
|
7
|
+
"exactOptionalPropertyTypes": true,
|
|
8
|
+
"target": "ES2022",
|
|
9
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
10
|
+
"module": "ESNext",
|
|
11
|
+
"moduleResolution": "Bundler",
|
|
12
|
+
"jsx": "preserve",
|
|
13
|
+
"allowJs": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": ["./src/*"]
|
|
22
|
+
},
|
|
23
|
+
"plugins": [{ "name": "next" }]
|
|
24
|
+
},
|
|
25
|
+
"include": ["src", "next-env.d.ts", ".next/types/**/*.ts"],
|
|
26
|
+
"exclude": ["node_modules"]
|
|
27
|
+
}
|