create-ec-app 0.0.1

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 ADDED
@@ -0,0 +1,204 @@
1
+ # create-ec-app
2
+
3
+ 🚀 **Unified CLI tool to create different types of EC applications**
4
+
5
+ Create React, Next.js, Power Pages, and React Native applications with a single command. Each app comes pre-configured with the tools and patterns you need for building enterprise applications.
6
+
7
+ ## Quick Start
8
+
9
+ ### npm (recommended)
10
+
11
+ ```bash
12
+ npx create-ec-app my-awesome-project
13
+ ```
14
+
15
+ ### bun
16
+
17
+ ```bash
18
+ bunx create-ec-app my-awesome-project
19
+ ```
20
+
21
+ ### pnpm
22
+
23
+ ```bash
24
+ pnpm create ec-app my-awesome-project
25
+ ```
26
+
27
+ ## App Types
28
+
29
+ ### 📱 Webresource App
30
+
31
+ Perfect for Dynamics 365 webresources
32
+
33
+ - **Framework**: React + Vite + TypeScript
34
+ - **Styling**: Tailwind CSS + Kendo UI
35
+ - **Features**:
36
+ - Pre-configured for Dynamics 365 embedding
37
+ - Kendo UI theme selection
38
+ - TanStack Query for data fetching
39
+ - Zustand for state management
40
+ - XRM type definitions
41
+
42
+ ### 🌐 Portal App
43
+
44
+ Ideal for customer portals and external-facing applications
45
+
46
+ - **Framework**: Next.js + TypeScript
47
+ - **Styling**: Choice of Kendo UI or Shadcn/ui
48
+ - **Features**:
49
+ - NextAuth.js authentication
50
+ - Dynamics 365 integration
51
+ - GitHub Actions workflow
52
+ - Environment configuration
53
+
54
+ ### ⚡ Power Pages App
55
+
56
+ Specialized for Microsoft Power Pages
57
+
58
+ - **Framework**: React + Vite + TypeScript
59
+ - **Styling**: Tailwind CSS + Kendo UI
60
+ - **Features**:
61
+ - Power Pages authentication context
62
+ - Portal-specific data services
63
+ - Pre-built auth components
64
+ - Power Pages configuration
65
+
66
+ ### 📲 Mobile App
67
+
68
+ Cross-platform mobile development
69
+
70
+ - **Framework**: React Native + Expo + TypeScript
71
+ - **Styling**: NativeWind (Tailwind for React Native)
72
+ - **Features**:
73
+ - MSAL.js authentication
74
+ - React Native Reusables components
75
+ - TanStack Query
76
+ - Pre-configured navigation
77
+
78
+ ## Installation & Usage
79
+
80
+ ### Option 1: Direct Usage (Recommended)
81
+
82
+ **npm**:
83
+
84
+ ```bash
85
+ npx create-ec-app@latest my-project
86
+ ```
87
+
88
+ **bun**:
89
+
90
+ ```bash
91
+ bunx create-ec-app@latest my-project
92
+ ```
93
+
94
+ **pnpm**:
95
+
96
+ ```bash
97
+ pnpm create ec-app@latest my-project
98
+ ```
99
+
100
+ ### Option 2: Global Installation
101
+
102
+ **npm**:
103
+
104
+ ```bash
105
+ npm install -g create-ec-app
106
+ create-ec-app my-project
107
+ ```
108
+
109
+ **bun**:
110
+
111
+ ```bash
112
+ bun add -g create-ec-app
113
+ create-ec-app my-project
114
+ ```
115
+
116
+ **pnpm**:
117
+
118
+ ```bash
119
+ pnpm add -g create-ec-app
120
+ create-ec-app my-project
121
+ ```
122
+
123
+ **yarn**:
124
+
125
+ ```bash
126
+ yarn global add create-ec-app
127
+ create-ec-app my-project
128
+ ```
129
+
130
+ ## Getting Started
131
+
132
+ 1. **Run the command**:
133
+
134
+ ```bash
135
+ npx create-ec-app my-project
136
+ # or with your preferred package manager
137
+ ```
138
+
139
+ 2. **Choose your application type** from the interactive menu
140
+
141
+ 3. **Follow the prompts** for additional configuration (themes, UI libraries, etc.)
142
+
143
+ 4. **Start developing**:
144
+ ```bash
145
+ cd my-project
146
+ npm run dev
147
+ # or: bun dev, pnpm dev, yarn dev
148
+ ```
149
+
150
+ ## What's Included
151
+
152
+ All applications come with:
153
+
154
+ - ✅ TypeScript configuration
155
+ - ✅ ESLint and Prettier setup
156
+ - ✅ Git repository initialization
157
+ - ✅ Environment configuration
158
+ - ✅ Build and development scripts
159
+ - ✅ Enterprise-ready folder structure
160
+
161
+ ### Webresource & Power Pages Apps Include:
162
+
163
+ - Kendo UI components and theming
164
+ - Dynamics 365 authentication helpers
165
+ - TanStack Query setup
166
+ - Zustand store configuration
167
+
168
+ ### Portal Apps Include:
169
+
170
+ - NextAuth.js configuration
171
+ - Choice of UI library (Kendo UI or Shadcn/ui)
172
+ - GitHub Actions deployment workflow example
173
+ - Environment variable templates
174
+
175
+ ### Mobile Apps Include:
176
+
177
+ - Expo development build configuration
178
+ - NativeWind styling system
179
+ - MSAL authentication setup
180
+ - Navigation structure
181
+
182
+ ## Development
183
+
184
+ To contribute to this tool:
185
+
186
+ ```bash
187
+ git clone <repository-url>
188
+ cd create-ec-app
189
+ npm install
190
+ npm run build
191
+ npm link
192
+ ```
193
+
194
+ ## License
195
+
196
+ MIT
197
+
198
+ ---
199
+
200
+ **Note**: For Kendo UI apps, remember to activate your license after project creation:
201
+
202
+ ```bash
203
+ npx kendo-ui-license activate
204
+ ```
package/bin/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
@@ -0,0 +1,2 @@
1
+ export declare const createMobileApp: (projectName: string) => Promise<void>;
2
+ //# sourceMappingURL=mobile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../../src/creators/mobile.ts"],"names":[],"mappings":"AAiMA,eAAO,MAAM,eAAe,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CA+CvE,CAAC"}
@@ -0,0 +1,220 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import { execSync } from "child_process";
6
+ // Helper function to run commands and show a spinner
7
+ const runCommand = (command, spinnerMessage) => {
8
+ const spinner = ora(spinnerMessage).start();
9
+ try {
10
+ execSync(command, { stdio: "pipe" });
11
+ spinner.succeed();
12
+ }
13
+ catch (error) {
14
+ spinner.fail();
15
+ console.error(chalk.red(`Failed to execute command: ${command}`));
16
+ console.error(error);
17
+ process.exit(1);
18
+ }
19
+ };
20
+ // Template functions
21
+ const getAppLayout = () => `import "@/global.css";
22
+ import FontAwesome from '@expo/vector-icons/FontAwesome';
23
+ import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
24
+ import { PortalHost } from '@rn-primitives/portal';
25
+ import { useFonts } from 'expo-font';
26
+ import { Stack } from 'expo-router';
27
+ import * as SplashScreen from 'expo-splash-screen';
28
+ import { useEffect } from 'react';
29
+ import 'react-native-reanimated';
30
+
31
+ import { useColorScheme } from '@/components/useColorScheme';
32
+
33
+ export {
34
+ // Catch any errors thrown by the Layout component.
35
+ ErrorBoundary
36
+ } from 'expo-router';
37
+
38
+ export const unstable_settings = {
39
+ // Ensure that reloading on '/modal' keeps a back button present.
40
+ initialRouteName: '(tabs)',
41
+ };
42
+
43
+ // Prevent the splash screen from auto-hiding before asset loading is complete.
44
+ SplashScreen.preventAutoHideAsync();
45
+
46
+ export default function RootLayout() {
47
+ const [loaded, error] = useFonts({
48
+ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
49
+ ...FontAwesome.font,
50
+ });
51
+
52
+ // Expo Router uses Error Boundaries to catch errors in the navigation tree.
53
+ useEffect(() => {
54
+ if (error) throw error;
55
+ }, [error]);
56
+
57
+ useEffect(() => {
58
+ if (loaded) {
59
+ SplashScreen.hideAsync();
60
+ }
61
+ }, [loaded]);
62
+
63
+ if (!loaded) {
64
+ return null;
65
+ }
66
+
67
+ return <RootLayoutNav />;
68
+ }
69
+
70
+ function RootLayoutNav() {
71
+ const colorScheme = useColorScheme();
72
+
73
+ return (
74
+ <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
75
+ <Stack>
76
+ <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
77
+ <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
78
+ </Stack>
79
+ <PortalHost />
80
+ </ThemeProvider>
81
+ );
82
+ }`;
83
+ const getThemeLib = () => `import { DarkTheme, DefaultTheme, type Theme } from '@react-navigation/native';
84
+
85
+ export const THEME = {
86
+ light: {
87
+ background: 'hsl(0 0% 100%)',
88
+ foreground: 'hsl(0 0% 3.9%)',
89
+ card: 'hsl(0 0% 100%)',
90
+ cardForeground: 'hsl(0 0% 3.9%)',
91
+ popover: 'hsl(0 0% 100%)',
92
+ popoverForeground: 'hsl(0 0% 3.9%)',
93
+ primary: 'hsl(0 0% 9%)',
94
+ primaryForeground: 'hsl(0 0% 98%)',
95
+ secondary: 'hsl(0 0% 96.1%)',
96
+ secondaryForeground: 'hsl(0 0% 9%)',
97
+ muted: 'hsl(0 0% 96.1%)',
98
+ mutedForeground: 'hsl(0 0% 45.1%)',
99
+ accent: 'hsl(0 0% 96.1%)',
100
+ accentForeground: 'hsl(0 0% 9%)',
101
+ destructive: 'hsl(0 84.2% 60.2%)',
102
+ border: 'hsl(0 0% 89.8%)',
103
+ input: 'hsl(0 0% 89.8%)',
104
+ ring: 'hsl(0 0% 63%)',
105
+ radius: '0.625rem',
106
+ chart1: 'hsl(12 76% 61%)',
107
+ chart2: 'hsl(173 58% 39%)',
108
+ chart3: 'hsl(197 37% 24%)',
109
+ chart4: 'hsl(43 74% 66%)',
110
+ chart5: 'hsl(27 87% 67%)',
111
+ },
112
+ dark: {
113
+ background: 'hsl(0 0% 3.9%)',
114
+ foreground: 'hsl(0 0% 98%)',
115
+ card: 'hsl(0 0% 3.9%)',
116
+ cardForeground: 'hsl(0 0% 98%)',
117
+ popover: 'hsl(0 0% 3.9%)',
118
+ popoverForeground: 'hsl(0 0% 98%)',
119
+ primary: 'hsl(0 0% 98%)',
120
+ primaryForeground: 'hsl(0 0% 9%)',
121
+ secondary: 'hsl(0 0% 14.9%)',
122
+ secondaryForeground: 'hsl(0 0% 98%)',
123
+ muted: 'hsl(0 0% 14.9%)',
124
+ mutedForeground: 'hsl(0 0% 63.9%)',
125
+ accent: 'hsl(0 0% 14.9%)',
126
+ accentForeground: 'hsl(0 0% 98%)',
127
+ destructive: 'hsl(0 70.9% 59.4%)',
128
+ border: 'hsl(0 0% 14.9%)',
129
+ input: 'hsl(0 0% 14.9%)',
130
+ ring: 'hsl(300 0% 45%)',
131
+ radius: '0.625rem',
132
+ chart1: 'hsl(220 70% 50%)',
133
+ chart2: 'hsl(160 60% 45%)',
134
+ chart3: 'hsl(30 80% 55%)',
135
+ chart4: 'hsl(280 65% 60%)',
136
+ chart5: 'hsl(340 75% 55%)',
137
+ },
138
+ };
139
+
140
+ export const NAV_THEME: Record<'light' | 'dark', Theme> = {
141
+ light: {
142
+ ...DefaultTheme,
143
+ colors: {
144
+ background: THEME.light.background,
145
+ border: THEME.light.border,
146
+ card: THEME.light.card,
147
+ notification: THEME.light.destructive,
148
+ primary: THEME.light.primary,
149
+ text: THEME.light.foreground,
150
+ },
151
+ },
152
+ dark: {
153
+ ...DarkTheme,
154
+ colors: {
155
+ background: THEME.dark.background,
156
+ border: THEME.dark.border,
157
+ card: THEME.dark.card,
158
+ notification: THEME.dark.destructive,
159
+ primary: THEME.dark.primary,
160
+ text: THEME.dark.foreground,
161
+ },
162
+ },
163
+ };`;
164
+ const getUtilsLib = () => `import { clsx, type ClassValue } from "clsx";
165
+ import { twMerge } from "tailwind-merge";
166
+
167
+ export function cn(...inputs: ClassValue[]) {
168
+ return twMerge(clsx(inputs));
169
+ }`;
170
+ const getComponentsJson = () => `{
171
+ "$schema": "https://ui.shadcn.com/schema.json",
172
+ "style": "new-york",
173
+ "rsc": false,
174
+ "tsx": true,
175
+ "tailwind": {
176
+ "config": "tailwind.config.js",
177
+ "css": "global.css",
178
+ "baseColor": "neutral",
179
+ "cssVariables": true
180
+ },
181
+ "aliases": {
182
+ "components": "@/components",
183
+ "utils": "@/lib/utils",
184
+ "ui": "@/components/ui",
185
+ "lib": "@/lib",
186
+ "hooks": "@/hooks"
187
+ }
188
+ }`;
189
+ export const createMobileApp = async (projectName) => {
190
+ const cleanProjectName = projectName
191
+ .trim()
192
+ .toLowerCase()
193
+ .replace(/\s+/g, "-")
194
+ .replace(/[^a-z0-9\-]/g, "-")
195
+ .replace(/-+/g, "-");
196
+ const projectDir = path.resolve(process.cwd(), cleanProjectName);
197
+ console.log(`\nScaffolding a new Expo app in ${chalk.green(projectDir)}...\n`);
198
+ runCommand(`npx create-expo-app@latest --example with-tailwindcss ${cleanProjectName}`, "Creating Expo project with Tailwind example...");
199
+ process.chdir(projectDir);
200
+ runCommand("npx expo install tailwindcss-animate class-variance-authority clsx tailwind-merge @rn-primitives/portal", "Installing React Native Reusables dependencies...");
201
+ const appLayoutPath = path.join(projectDir, "app", "_layout.tsx");
202
+ await fs.writeFile(appLayoutPath, getAppLayout(), "utf8");
203
+ ora("Updated app/_layout.tsx").succeed();
204
+ const libDir = path.join(projectDir, "lib");
205
+ await fs.ensureDir(libDir);
206
+ const themePath = path.join(libDir, "theme.ts");
207
+ await fs.writeFile(themePath, getThemeLib(), "utf8");
208
+ ora("Created lib/theme.ts").succeed();
209
+ const utilsPath = path.join(libDir, "utils.ts");
210
+ await fs.writeFile(utilsPath, getUtilsLib(), "utf8");
211
+ ora("Created lib/utils.ts").succeed();
212
+ const componentsJsonPath = path.join(projectDir, "components.json");
213
+ await fs.writeFile(componentsJsonPath, getComponentsJson(), "utf8");
214
+ ora("Created components.json").succeed();
215
+ // Initialize Git
216
+ runCommand("git init", "Initializing Git repository...");
217
+ runCommand("git add .", "Staging files for initial commit...");
218
+ runCommand(`git commit -m "Initial commit from create-ec-app"`, "Creating initial commit...");
219
+ };
220
+ //# sourceMappingURL=mobile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.js","sourceRoot":"","sources":["../../src/creators/mobile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const createPortalApp: (projectName: string) => Promise<void>;
2
+ //# sourceMappingURL=portal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/creators/portal.ts"],"names":[],"mappings":"AAuuBA,eAAO,MAAM,eAAe,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAuNvE,CAAC"}