nl-d365boilerplate-vite 1.0.0 ā 1.0.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 +90 -61
- package/bin/cli.js +86 -67
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,75 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# š novalogica | Dynamics 365 React Boilerplate
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://vite.dev/)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://tailwindcss.com/)
|
|
8
|
+
[](https://ui.shadcn.com/)
|
|
6
9
|
|
|
7
|
-
-
|
|
8
|
-
|
|
10
|
+
**Production-ready React 19 boilerplate built with Vite for Microsoft Dynamics 365 development.**
|
|
11
|
+
*Modern, type-safe, and designed for seamless Dataverse integration.*
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
[šÆ Quick Start](#-quick-start) ⢠[š§ Features](#-features) ⢠[š Documentation](#-documentation)
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
</div>
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
---
|
|
15
18
|
|
|
16
|
-
##
|
|
19
|
+
## šÆ Quick Start
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
### ā” Create a New Project
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
export default defineConfig([
|
|
22
|
-
globalIgnores(['dist']),
|
|
23
|
-
{
|
|
24
|
-
files: ['**/*.{ts,tsx}'],
|
|
25
|
-
extends: [
|
|
26
|
-
// Other configs...
|
|
23
|
+
Scaffold your production-ready app in seconds using our interactive CLI:
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
```bash
|
|
26
|
+
# Initialize a new project
|
|
27
|
+
npx nl-d365boilerplate-vite@latest my-app-name
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### š Start Developing
|
|
31
|
+
|
|
32
|
+
Once created, setting up your environment is simple:
|
|
33
|
+
|
|
34
|
+
1. **Enter your project**:
|
|
35
|
+
```bash
|
|
36
|
+
cd my-app-name
|
|
37
|
+
npm install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
2. **Configure Environment**:
|
|
41
|
+
```bash
|
|
42
|
+
cp .env.example .env
|
|
43
|
+
```
|
|
44
|
+
*Update `.env` with your D365 Environment URL and Client ID.*
|
|
45
|
+
|
|
46
|
+
3. **Run Development Server**:
|
|
47
|
+
```bash
|
|
48
|
+
npm run dev
|
|
49
|
+
```
|
|
50
|
+
*Automatically handles OAuth and launches your app at `http://localhost:5173`.*
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## š§ Features
|
|
55
|
+
|
|
56
|
+
| Feature | Description |
|
|
57
|
+
| :--- | :--- |
|
|
58
|
+
| **āļø React 19 + Vite 7** | Built on the latest, fastest web standards |
|
|
59
|
+
| **šØ Tailwind CSS 4** | Next-gen utility-first styling with Shadcn/UI components |
|
|
60
|
+
| **š DynamicsWebApi** | Type-safe Dataverse wrapper with OAuth integration |
|
|
61
|
+
| **š ļø TypeScript** | Complete type safety for reliable code |
|
|
62
|
+
| **š§ Built-in Routing** | Configurable page routing and navigation hooks |
|
|
63
|
+
| **š¦ Web Resource Ready** | Optimized build scripts for D365 deployment |
|
|
64
|
+
|
|
65
|
+
---
|
|
34
66
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
## š Documentation
|
|
68
|
+
|
|
69
|
+
### Interactive Docs
|
|
70
|
+
Your new project comes with **built-in interactive documentation**.
|
|
71
|
+
Run `npm start` and navigate to the **Documentation** page for live examples.
|
|
72
|
+
|
|
73
|
+
### Project Structure
|
|
74
|
+
```text
|
|
75
|
+
src/
|
|
76
|
+
āāā š components/ # Shadcn/UI & shared components
|
|
77
|
+
āāā š config/ # App configuration & page routing
|
|
78
|
+
āāā š hooks/ # Custom hooks (e.g., useDataverse)
|
|
79
|
+
āāā š pages/ # Application views
|
|
80
|
+
āāā š providers/ # React Context providers (Theme, Auth)
|
|
81
|
+
āāā š lib/ # Utilities
|
|
46
82
|
```
|
|
47
83
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
globalIgnores(['dist']),
|
|
57
|
-
{
|
|
58
|
-
files: ['**/*.{ts,tsx}'],
|
|
59
|
-
extends: [
|
|
60
|
-
// Other configs...
|
|
61
|
-
// Enable lint rules for React
|
|
62
|
-
reactX.configs['recommended-typescript'],
|
|
63
|
-
// Enable lint rules for React DOM
|
|
64
|
-
reactDom.configs.recommended,
|
|
65
|
-
],
|
|
66
|
-
languageOptions: {
|
|
67
|
-
parserOptions: {
|
|
68
|
-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
69
|
-
tsconfigRootDir: import.meta.dirname,
|
|
70
|
-
},
|
|
71
|
-
// other options...
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
])
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## š Deployment
|
|
87
|
+
|
|
88
|
+
Build your application for Dynamics 365 Web Resources:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm run build
|
|
75
92
|
```
|
|
93
|
+
|
|
94
|
+
**Output:**
|
|
95
|
+
- `build/index.html` ā HTML Web Resource
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
<div align="center">
|
|
100
|
+
|
|
101
|
+
**[ā Star on GitHub](https://github.com/novalogica/nl-dynamics-boilerplate)**
|
|
102
|
+
Made with ā¤ļø by [novalogica](https://novalogica.pt)
|
|
103
|
+
|
|
104
|
+
</div>
|
package/bin/cli.js
CHANGED
|
@@ -1,84 +1,103 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
+
import { intro, outro, text, isCancel, cancel } from "@clack/prompts";
|
|
6
7
|
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = path.dirname(__filename);
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
async function main() {
|
|
12
|
+
intro(`š Create NL D365 Boilerplate`);
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
console.error("Please specify the project directory:");
|
|
14
|
-
console.error(" npm create nl-d365boilerplate-vite <project-directory>");
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const targetPath = path.join(process.cwd(), targetDirName);
|
|
19
|
-
const templatePath = path.join(__dirname, "..");
|
|
14
|
+
let targetDirName = process.argv[2];
|
|
20
15
|
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
if (!targetDirName) {
|
|
17
|
+
const projectName = await text({
|
|
18
|
+
message: "What is the name of your project?",
|
|
19
|
+
placeholder: "my-d365-app",
|
|
20
|
+
validate(value) {
|
|
21
|
+
if (value.length === 0) return "Value is required!";
|
|
22
|
+
},
|
|
23
|
+
});
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const SKIP_FILES = [
|
|
30
|
-
"node_modules",
|
|
31
|
-
".git",
|
|
32
|
-
".env",
|
|
33
|
-
"package-lock.json",
|
|
34
|
-
"bin",
|
|
35
|
-
"dist",
|
|
36
|
-
".npmignore",
|
|
37
|
-
".DS_Store",
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
function copyDir(src, dest) {
|
|
41
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
42
|
-
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
const srcPath = path.join(src, entry.name);
|
|
45
|
-
const destPath = path.join(dest, entry.name);
|
|
46
|
-
|
|
47
|
-
if (SKIP_FILES.includes(entry.name)) {
|
|
48
|
-
continue;
|
|
25
|
+
if (isCancel(projectName)) {
|
|
26
|
+
cancel("Operation cancelled.");
|
|
27
|
+
process.exit(0);
|
|
49
28
|
}
|
|
50
29
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
30
|
+
targetDirName = projectName;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const targetPath = path.join(process.cwd(), targetDirName);
|
|
34
|
+
const templatePath = path.join(__dirname, "..");
|
|
35
|
+
|
|
36
|
+
if (fs.existsSync(targetPath)) {
|
|
37
|
+
console.error(`\nā Directory "${targetDirName}" already exists.`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(`\nCreating a new app in ${targetPath}...`);
|
|
42
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
43
|
+
|
|
44
|
+
const SKIP_FILES = [
|
|
45
|
+
"node_modules",
|
|
46
|
+
".git",
|
|
47
|
+
".env",
|
|
48
|
+
"package-lock.json",
|
|
49
|
+
"bin",
|
|
50
|
+
"dist",
|
|
51
|
+
".npmignore",
|
|
52
|
+
".DS_Store",
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
function copyDir(src, dest) {
|
|
56
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
57
|
+
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const srcPath = path.join(src, entry.name);
|
|
60
|
+
const destPath = path.join(dest, entry.name);
|
|
61
|
+
|
|
62
|
+
if (SKIP_FILES.includes(entry.name)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Prevent copying the new project directory into itself
|
|
67
|
+
if (path.resolve(srcPath) === path.resolve(targetPath)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (entry.isDirectory()) {
|
|
72
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
73
|
+
copyDir(srcPath, destPath);
|
|
74
|
+
} else {
|
|
75
|
+
fs.copyFileSync(srcPath, destPath);
|
|
76
|
+
}
|
|
56
77
|
}
|
|
57
78
|
}
|
|
79
|
+
|
|
80
|
+
// Copy files
|
|
81
|
+
copyDir(templatePath, targetPath);
|
|
82
|
+
|
|
83
|
+
// Update package.json
|
|
84
|
+
const pkgJsonPath = path.join(targetPath, "package.json");
|
|
85
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
86
|
+
|
|
87
|
+
pkg.name = targetDirName;
|
|
88
|
+
pkg.version = "0.0.0";
|
|
89
|
+
pkg.private = true;
|
|
90
|
+
delete pkg.bin;
|
|
91
|
+
delete pkg.files;
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2));
|
|
94
|
+
|
|
95
|
+
outro(`You're all set!`);
|
|
96
|
+
|
|
97
|
+
console.log(`Next steps:`);
|
|
98
|
+
console.log(` cd ${targetDirName}`);
|
|
99
|
+
console.log(` npm install`);
|
|
100
|
+
console.log(` npm run dev`);
|
|
58
101
|
}
|
|
59
102
|
|
|
60
|
-
|
|
61
|
-
copyDir(templatePath, targetPath);
|
|
62
|
-
|
|
63
|
-
// Update package.json
|
|
64
|
-
const pkgJsonPath = path.join(targetPath, "package.json");
|
|
65
|
-
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
66
|
-
|
|
67
|
-
pkg.name = targetDirName;
|
|
68
|
-
pkg.version = "0.0.0";
|
|
69
|
-
pkg.private = true;
|
|
70
|
-
delete pkg.bin;
|
|
71
|
-
delete pkg.files;
|
|
72
|
-
|
|
73
|
-
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2));
|
|
74
|
-
|
|
75
|
-
console.log("\nSuccess! Created project at " + targetPath);
|
|
76
|
-
console.log("Inside that directory, you can run several commands:\n");
|
|
77
|
-
console.log(" npm install");
|
|
78
|
-
console.log(" Installs dependencies.\n");
|
|
79
|
-
console.log(" npm run dev");
|
|
80
|
-
console.log(" Starts the development server.\n");
|
|
81
|
-
console.log("\nWe suggest that you begin by typing:\n");
|
|
82
|
-
console.log(` cd ${targetDirName}`);
|
|
83
|
-
console.log(" npm install");
|
|
84
|
-
console.log(" npm run dev");
|
|
103
|
+
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nl-d365boilerplate-vite",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nl-d365boilerplate-vite": "./bin/cli.js"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"lint:fix": "eslint . --fix"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@clack/prompts": "^0.11.0",
|
|
34
35
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
35
36
|
"@radix-ui/react-label": "^2.1.8",
|
|
36
37
|
"@radix-ui/react-separator": "^1.1.8",
|