@eui/cli 22.0.0-alpha.21 → 22.0.0-alpha.23

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.
Files changed (39) hide show
  1. package/README.md +303 -90
  2. package/lib/app-utils/build.js +26 -0
  3. package/lib/app-utils/serve.js +32 -0
  4. package/lib/build.js +28 -7
  5. package/lib/cli.js +16 -2
  6. package/lib/config.js +10 -1
  7. package/lib/skeletons/_angular/base/package.json +3 -2
  8. package/lib/skeletons/_angular/base-mobile/package.json +1 -1
  9. package/lib/skeletons/_angular/options/eds/src/app/app.component.html +35 -0
  10. package/lib/skeletons/_angular/options/eds/src/app/app.component.spec.ts +56 -0
  11. package/lib/skeletons/_angular/options/eds/src/app/app.component.ts +44 -0
  12. package/lib/skeletons/_angular/options/eds/src/index.html +21 -0
  13. package/lib/skeletons/_angular/options/pnpm/package.json +97 -47
  14. package/lib/skeletons/_react/base/README.md +65 -0
  15. package/lib/skeletons/_react/base/eslint.config.mjs +17 -0
  16. package/lib/skeletons/_react/base/gitignore_TO_REPLACE +34 -0
  17. package/lib/skeletons/_react/base/index.html +13 -0
  18. package/lib/skeletons/_react/base/mock/app/models/user.js +6 -0
  19. package/lib/skeletons/_react/base/mock/app/routes/index.js +5 -0
  20. package/lib/skeletons/_react/base/mock/app/routes/user_routes.js +44 -0
  21. package/lib/skeletons/_react/base/mock/db/db.json +10 -0
  22. package/lib/skeletons/_react/base/mock/server.js +23 -0
  23. package/lib/skeletons/_react/base/package.json +29 -0
  24. package/lib/skeletons/_react/base/public/.gitkeep +0 -0
  25. package/lib/skeletons/_react/base/src/App.tsx +73 -0
  26. package/lib/skeletons/_react/base/src/main.tsx +30 -0
  27. package/lib/skeletons/_react/base/src/pages/about.tsx +27 -0
  28. package/lib/skeletons/_react/base/src/pages/home.tsx +50 -0
  29. package/lib/skeletons/_react/base/src/routes.ts +18 -0
  30. package/lib/skeletons/_react/base/src/styles.css +37 -0
  31. package/lib/skeletons/_react/base/src/vite-env.d.ts +1 -0
  32. package/lib/skeletons/_react/base/tsconfig.app.json +18 -0
  33. package/lib/skeletons/_react/base/tsconfig.json +7 -0
  34. package/lib/skeletons/_react/base/tsconfig.node.json +15 -0
  35. package/lib/skeletons/_react/base/vite.config.ts +77 -0
  36. package/lib/skeletons/_react/options/pnpm/package.json +45 -0
  37. package/lib/utils.js +24 -0
  38. package/package.json +1 -1
  39. package/sonar-project.properties +0 -14
@@ -0,0 +1,73 @@
1
+ import { Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom';
2
+ import {
3
+ EdsShell,
4
+ EdsShellToolbar,
5
+ EdsShellToolbarApp,
6
+ EdsShellMenuToggle,
7
+ EdsShellSidebar,
8
+ EdsShellSidebarNav,
9
+ EdsShellSidebarFooter,
10
+ EdsShellContent,
11
+ EdsShellFooter,
12
+ type EdsMenuItem,
13
+ } from '@eui/eds-react/eds-shell';
14
+
15
+ import { NAV_ITEMS } from './routes.js';
16
+ import { HomePage } from './pages/home.js';
17
+ import { AboutPage } from './pages/about.js';
18
+
19
+ /**
20
+ * The application shell — an `EdsShell` with a toolbar, a collapsible sidebar navigation, and a
21
+ * routed content area. Routing is owned by the app (React Router `HashRouter`, set up in
22
+ * `main.tsx`): the sidebar's `onItemClick` navigates, and `<Routes>` renders the matching page.
23
+ * The `EdsShell` component is router-agnostic — nav items only carry a `url` and fire
24
+ * `onItemClick`; the app decides what to do with it.
25
+ */
26
+ export function App() {
27
+ const navigate = useNavigate();
28
+ const location = useLocation();
29
+ const currentPath = location.pathname === '/' ? '/home' : location.pathname;
30
+
31
+ // Build the sidebar items from the shared route table, flagging the active one.
32
+ const navItems: EdsMenuItem[] = NAV_ITEMS.map((item) => ({
33
+ label: item.label,
34
+ icon: item.icon,
35
+ url: `#${item.path}`,
36
+ active: item.path === currentPath,
37
+ }));
38
+
39
+ const onNavClick = (item: EdsMenuItem): void => {
40
+ if (!item.url) {
41
+ return;
42
+ }
43
+ navigate(item.url.replace(/^#/, '') || '/');
44
+ };
45
+
46
+ return (
47
+ <EdsShell
48
+ toolbar={
49
+ <EdsShellToolbar
50
+ menuToggle={<EdsShellMenuToggle />}
51
+ app={<EdsShellToolbarApp appName="eUI React App" appSubTitle="@eui/eds-react" />}
52
+ />
53
+ }
54
+ sidebar={
55
+ <EdsShellSidebar
56
+ collapseMode="icons"
57
+ nav={<EdsShellSidebarNav items={navItems} hasIcons onItemClick={onNavClick} />}
58
+ footer={<EdsShellSidebarFooter>Powered by @eui/eds-react</EdsShellSidebarFooter>}
59
+ />
60
+ }
61
+ footer={<EdsShellFooter copyright="© European Union" />}
62
+ >
63
+ <EdsShellContent>
64
+ <Routes>
65
+ <Route path="/home" element={<HomePage />} />
66
+ <Route path="/about" element={<AboutPage />} />
67
+ <Route path="/" element={<Navigate to="/home" replace />} />
68
+ <Route path="*" element={<Navigate to="/home" replace />} />
69
+ </Routes>
70
+ </EdsShellContent>
71
+ </EdsShell>
72
+ );
73
+ }
@@ -0,0 +1,30 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { HashRouter } from 'react-router-dom';
4
+
5
+ // EdsIcon resolves sprites from `${assetsBase}/eds/icons/sprites/{set}.svg`. Derive an absolute
6
+ // base from Vite's deploy base (`import.meta.env.BASE_URL`) so sprites load regardless of the
7
+ // page URL's trailing slash. The eds-assets Vite plugin serves the sprites under `/assets/eds`.
8
+ (globalThis as { __EDS_ASSETS_BASE__?: string }).__EDS_ASSETS_BASE__ =
9
+ `${import.meta.env.BASE_URL.replace(/\/$/, '')}/assets`;
10
+
11
+ // EDS design tokens + base styles (provide the --eds-* custom properties the components read)
12
+ // and the eds-u-* layout utilities.
13
+ import '@eui/eds-styles/dist/eds.css';
14
+ import '@eui/eds-styles/dist/eds-utilities.css';
15
+ // All pure-React @eui/eds-react component styles via the combined barrel. Bundle-conscious apps
16
+ // can instead import only the per-component CSS they use (e.g. '@eui/eds-react/eds-button/eds-button.css').
17
+ import '@eui/eds-react/styles.css';
18
+
19
+ // App-specific styles.
20
+ import './styles.css';
21
+
22
+ import { App } from './App.js';
23
+
24
+ createRoot(document.getElementById('root')!).render(
25
+ <StrictMode>
26
+ <HashRouter>
27
+ <App />
28
+ </HashRouter>
29
+ </StrictMode>,
30
+ );
@@ -0,0 +1,27 @@
1
+ import { EdsCard, EdsCardHeader, EdsCardLabel, EdsCardBody } from '@eui/eds-react/eds-card';
2
+
3
+ /**
4
+ * A second example page, reachable from the sidebar, to demonstrate client-side routing.
5
+ */
6
+ export function AboutPage() {
7
+ return (
8
+ <div className="app-page">
9
+ <h1 className="app-page__title">About</h1>
10
+ <p className="app-page__intro">
11
+ This page is served by React Router. Navigation between it and the home page happens
12
+ entirely client-side via the sidebar, with no full-page reload.
13
+ </p>
14
+
15
+ <EdsCard>
16
+ <EdsCardHeader>
17
+ <EdsCardLabel>How routing works</EdsCardLabel>
18
+ </EdsCardHeader>
19
+ <EdsCardBody>
20
+ The sidebar items in <code>src/App.tsx</code> carry a <code>url</code> and the
21
+ shell calls <code>onItemClick</code>; the app then navigates with React Router.
22
+ The <code>EdsShell</code> itself stays router-agnostic.
23
+ </EdsCardBody>
24
+ </EdsCard>
25
+ </div>
26
+ );
27
+ }
@@ -0,0 +1,50 @@
1
+ import { EdsCard, EdsCardHeader, EdsCardLabel, EdsCardBody } from '@eui/eds-react/eds-card';
2
+ import { EdsButton } from '@eui/eds-react/eds-button';
3
+
4
+ /**
5
+ * The default landing page. A simple example composing a couple of `@eui/eds-react` components
6
+ * (`EdsCard`, `EdsButton`) inside the shell content area. Replace this with your own home page.
7
+ */
8
+ export function HomePage() {
9
+ return (
10
+ <div className="app-page">
11
+ <h1 className="app-page__title">Welcome to your eUI React app</h1>
12
+ <p className="app-page__intro">
13
+ This starter is powered by <strong>@eui/eds-react</strong> — the pure-React port of the
14
+ EU Design System components — with routing handled by React Router.
15
+ </p>
16
+
17
+ <div className="eds-u-d-flex eds-u-flex-wrap eds-u-flex-gap-l">
18
+ <EdsCard>
19
+ <EdsCardHeader>
20
+ <EdsCardLabel>Get started</EdsCardLabel>
21
+ </EdsCardHeader>
22
+ <EdsCardBody>
23
+ Edit <code>src/pages/home.tsx</code> to build your first page, then add routes in
24
+ <code> src/routes.ts</code> and <code>src/App.tsx</code>.
25
+ </EdsCardBody>
26
+ </EdsCard>
27
+
28
+ <EdsCard>
29
+ <EdsCardHeader>
30
+ <EdsCardLabel>Components</EdsCardLabel>
31
+ </EdsCardHeader>
32
+ <EdsCardBody>
33
+ Import any component from <code>@eui/eds-react</code> and its styles are already
34
+ loaded via the combined <code>styles.css</code> barrel in <code>main.tsx</code>.
35
+ </EdsCardBody>
36
+ </EdsCard>
37
+ </div>
38
+
39
+ <div className="eds-u-mt-l eds-u-d-flex eds-u-flex-gap-s">
40
+ <EdsButton
41
+ type="primary"
42
+ onClick={() => window.open('https://eui.ecdevops.eu', '_blank', 'noopener')}
43
+ >
44
+ eUI documentation
45
+ </EdsButton>
46
+ <EdsButton type="secondary">Secondary action</EdsButton>
47
+ </div>
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The app's navigation routes. The sidebar (in `App.tsx`) and the routed pages both derive from
3
+ * this single table, so adding a page is a one-line change here plus a `<Route>` in `App.tsx`.
4
+ * `icon` values are EDS icon names (see the sprite catalog in `@eui/eds-styles`).
5
+ */
6
+ export interface NavItem {
7
+ /** Router path (also the hash route). */
8
+ path: string;
9
+ /** Sidebar label. */
10
+ label: string;
11
+ /** EDS icon name shown in the sidebar. */
12
+ icon: string;
13
+ }
14
+
15
+ export const NAV_ITEMS: NavItem[] = [
16
+ { path: '/home', label: 'Home', icon: 'house' },
17
+ { path: '/about', label: 'About', icon: 'circle-info' },
18
+ ];
@@ -0,0 +1,37 @@
1
+ /*
2
+ * App-specific styles. The EDS design tokens (--eds-* custom properties) come from
3
+ * @eui/eds-styles (loaded in main.tsx) and are available anywhere under the `eui-21` scope set
4
+ * on <body> in index.html. Prefer the eds-u-* utility classes for layout; use this file only
5
+ * for app-level chrome.
6
+ */
7
+
8
+ html,
9
+ body,
10
+ #root {
11
+ height: 100%;
12
+ margin: 0;
13
+ }
14
+
15
+ .app-page {
16
+ padding: var(--eds-sp-l);
17
+ max-width: 960px;
18
+ }
19
+
20
+ .app-page__title {
21
+ font: var(--eds-f-heading-l);
22
+ color: var(--eds-c-foreground);
23
+ margin: 0 0 var(--eds-sp-s);
24
+ }
25
+
26
+ .app-page__intro {
27
+ font: var(--eds-f-paragraph-m);
28
+ color: var(--eds-c-foreground-subtle);
29
+ margin: 0 0 var(--eds-sp-l);
30
+ }
31
+
32
+ .app-page code {
33
+ font-family: monospace;
34
+ background: var(--eds-c-surface-elevation-sunken);
35
+ border-radius: var(--eds-br-xs);
36
+ padding: 0 var(--eds-sp-3xs);
37
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true,
11
+ "isolatedModules": true,
12
+ "verbatimModuleSyntax": true,
13
+ "noUnusedLocals": true,
14
+ "noUnusedParameters": true,
15
+ "noFallthroughCasesInSwitch": true
16
+ },
17
+ "include": ["src"]
18
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ "isolatedModules": true,
11
+ "verbatimModuleSyntax": true,
12
+ "types": ["node"]
13
+ },
14
+ "include": ["vite.config.ts"]
15
+ }
@@ -0,0 +1,77 @@
1
+ import { defineConfig, type Plugin } from 'vite';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { cpSync, createReadStream, existsSync, statSync } from 'node:fs';
5
+ import type { IncomingMessage, ServerResponse } from 'node:http';
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url));
8
+ const outDir = resolve(here, 'dist');
9
+
10
+ // EDS design assets (icon sprites, fonts, the shared eds-components.json catalog) ship inside the
11
+ // installed `@eui/eds-styles` package. `EdsIcon` resolves sprites from
12
+ // `${assetsBase}/eds/icons/sprites/{set}.svg`, so the package's `dist/assets` folder is exposed
13
+ // under `/assets/eds` for both the dev server and the built output.
14
+ const edsStylesAssets = resolve(here, 'node_modules/@eui/eds-styles/dist/assets');
15
+
16
+ /**
17
+ * Serve + emit the EDS assets under `/assets/eds` so icon sprites resolve in dev and in the
18
+ * built bundle. Do NOT commit the sprite/font binaries into the app — they always come from the
19
+ * `@eui/eds-styles` package.
20
+ */
21
+ function edsAssetsPlugin(assetsDir: string): Plugin {
22
+ const mime: Record<string, string> = {
23
+ '.svg': 'image/svg+xml',
24
+ '.json': 'application/json',
25
+ '.woff2': 'font/woff2',
26
+ '.woff': 'font/woff',
27
+ };
28
+ return {
29
+ name: 'eds-assets',
30
+ configureServer(server) {
31
+ server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {
32
+ const url = (req.url ?? '').split('?')[0];
33
+ const prefix = '/assets/eds/';
34
+ if (!url.startsWith(prefix)) {
35
+ return next();
36
+ }
37
+ const file = join(assetsDir, url.slice(prefix.length));
38
+ if (!existsSync(file) || !statSync(file).isFile()) {
39
+ return next();
40
+ }
41
+ const ext = file.slice(file.lastIndexOf('.'));
42
+ res.setHeader('Content-Type', mime[ext] ?? 'application/octet-stream');
43
+ createReadStream(file).pipe(res);
44
+ });
45
+ },
46
+ closeBundle() {
47
+ if (existsSync(assetsDir)) {
48
+ cpSync(assetsDir, join(outDir, 'assets', 'eds'), { recursive: true });
49
+ }
50
+ },
51
+ };
52
+ }
53
+
54
+ // Relative asset base (`./`) so the built app works under any deploy sub-path. JSX uses Vite's
55
+ // built-in esbuild automatic runtime (no @vitejs/plugin-react needed).
56
+ export default defineConfig({
57
+ root: here,
58
+ base: './',
59
+ publicDir: resolve(here, 'public'),
60
+ esbuild: { jsx: 'automatic' },
61
+ plugins: [edsAssetsPlugin(edsStylesAssets)],
62
+ build: {
63
+ outDir,
64
+ emptyOutDir: true,
65
+ },
66
+ server: {
67
+ port: 4200,
68
+ open: '/',
69
+ // Proxy API calls to the mock json-server (started in parallel via `npm start`).
70
+ proxy: {
71
+ '/api': {
72
+ target: 'http://localhost:3000',
73
+ changeOrigin: true,
74
+ },
75
+ },
76
+ },
77
+ });
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "eui-react-app",
3
+ "version": "22.0.0-alpha.23",
4
+ "license": "EUPL-1.1",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "start-mock-server": "nodemon --watch ./mock ./mock/server.js",
9
+ "start-serve": "eui-cli serve-app",
10
+ "start": "npm-run-all --parallel start-mock-server start-serve",
11
+ "start-local": "eui-cli serve-app",
12
+ "build": "tsc -b && eui-cli build-app",
13
+ "preview": "vite preview",
14
+ "lint": "eslint src --ext .ts,.tsx",
15
+ "test": "vitest run"
16
+ },
17
+ "dependencies": {
18
+ "react": "19.3.0",
19
+ "react-dom": "19.3.0",
20
+ "react-router-dom": "7.18.4",
21
+ "luxon": "3.7.2",
22
+ "@eui/eds-styles": "22.0.0-alpha.23",
23
+ "@eui/eds-react": "22.0.0-alpha.23"
24
+ },
25
+ "devDependencies": {
26
+ "@eui/cli": "22.0.0-alpha.23",
27
+ "typescript": "6.0.3",
28
+ "@types/react": "19.3.0",
29
+ "@types/react-dom": "19.3.0",
30
+ "@types/luxon": "3.7.2",
31
+ "vitest": "4.1.11",
32
+ "@vitest/ui": "4.1.11",
33
+ "@vitest/coverage-v8": "4.1.11",
34
+ "@vitest/coverage-istanbul": "4.1.11",
35
+ "eslint": "9.39.1",
36
+ "typescript-eslint": "8.63.0",
37
+ "@eslint/js": "9.39.1",
38
+ "npm-run-all": "4.1.5",
39
+ "json-server": "1.0.0-beta.3",
40
+ "nodemon": "3.1.11",
41
+ "lowdb": "1.0.0",
42
+ "body-parser": "1.20.3",
43
+ "express": "4.21.2"
44
+ }
45
+ }
package/lib/utils.js CHANGED
@@ -9,6 +9,7 @@ module.exports.getAngularPath = (targetPath, appName, appType, artifactId) => {
9
9
  switch(appType) {
10
10
  case 'angular':
11
11
  case 'mobile':
12
+ case 'react':
12
13
  return targetPath;
13
14
  case 'web-maven':
14
15
  return `${targetPath}/src/main/angular`;
@@ -16,9 +17,32 @@ module.exports.getAngularPath = (targetPath, appName, appType, artifactId) => {
16
17
  return `${targetPath}/${artifactId}-web/src/main/angular`;
17
18
  case 'web-symfony':
18
19
  return `${targetPath}/client`;
20
+ default:
21
+ throw new Error(`Unknown appType: "${appType}"`);
19
22
  }
20
23
  }
21
24
 
25
+ /**
26
+ * Detects the toolchain of the app in `cwd`. A generated eUI app is either an Angular app
27
+ * (has `angular.json`) or a standalone Vite React app (has `vite.config.ts`/`.js` and no
28
+ * `angular.json`). Returns `'vite'` or `'angular'` so the serve/build commands can dispatch to
29
+ * the right tool while keeping a single `eui-cli serve-app` / `build-app` command surface.
30
+ */
31
+ module.exports.detectAppTool = (cwd = process.cwd()) => {
32
+ if (isFileExists(path.join(cwd, 'angular.json'))) {
33
+ return 'angular';
34
+ }
35
+ if (
36
+ isFileExists(path.join(cwd, 'vite.config.ts')) ||
37
+ isFileExists(path.join(cwd, 'vite.config.js')) ||
38
+ isFileExists(path.join(cwd, 'vite.config.mjs'))
39
+ ) {
40
+ return 'vite';
41
+ }
42
+ // Default to Angular for backward compatibility with existing generated apps.
43
+ return 'angular';
44
+ };
45
+
22
46
  module.exports.getArgs = () => {
23
47
  const argv = {};
24
48
  const processArgs = process.env._CSDR_ARGS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eui/cli",
3
- "version": "22.0.0-alpha.21",
3
+ "version": "22.0.0-alpha.23",
4
4
  "tag": "next",
5
5
  "license": "EUPL-1.1",
6
6
  "description": "eUI CLI app generator & tools",
@@ -1,14 +0,0 @@
1
- sonar.projectKey=CSDR-EUI-CLI
2
- sonar.projectName=EUI-CLI
3
- sonar.projectVersion=375ec32
4
-
5
-
6
- sonar.sources=/builds/csdr/eui/eui/packages/cli
7
- sonar.sourceEncoding=UTF-8
8
-
9
- sonar.test.inclusions=**/*.spec.ts
10
-
11
- sonar.ts.tslint.configPath=tslint.json
12
- sonar.typescript.lcov.reportPaths=/builds/csdr/eui/eui/packages/cli/test/reports/coverage-lcov/lcov.info
13
-
14
- sonar.exclusions=**/*.js, **/mock/**/*, **/mocks/**/*