create-middag-ui 0.26.1 → 0.27.0
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/cli.js +28 -2
- package/lib/detect.js +28 -0
- package/lib/scaffold.js +51 -3
- package/lib/scaffoldPRO.js +2 -2
- package/lib/templates/pro/main.tsx +2 -2
- package/lib/templates/pro/register-pro.ts +14 -7
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { join } from "node:path";
|
|
23
|
-
import { detectHost, detectMoodleComponent, HOSTS } from "./lib/detect.js";
|
|
23
|
+
import { detectHost, detectMoodleComponent, detectPhpHost, HOSTS } from "./lib/detect.js";
|
|
24
24
|
import { ask, confirm, select } from "./lib/prompts.js";
|
|
25
25
|
import { runTokenFlow } from "./lib/auth.js";
|
|
26
26
|
import {
|
|
@@ -92,6 +92,32 @@ if (hostKey) {
|
|
|
92
92
|
|
|
93
93
|
const host = HOSTS[hostKey];
|
|
94
94
|
|
|
95
|
+
// Custom host: probe for a PHP backend so the production build targets the
|
|
96
|
+
// PHP doc-root (public/build) with a base-href that keeps lazy chunks resolving.
|
|
97
|
+
let phpHost = null;
|
|
98
|
+
if (hostKey === "custom") {
|
|
99
|
+
const php = detectPhpHost(cwd);
|
|
100
|
+
if (php && php.docRoot) {
|
|
101
|
+
const fwLabel = php.framework ? ` (${php.framework})` : "";
|
|
102
|
+
if (nonInteractive) {
|
|
103
|
+
phpHost = php;
|
|
104
|
+
success(`PHP host detected${fwLabel} → build to public/build (--yes mode)`);
|
|
105
|
+
} else {
|
|
106
|
+
const docRel = php.docRoot.replace(cwd, ".");
|
|
107
|
+
const ok = await confirm(
|
|
108
|
+
`PHP backend detected${fwLabel}. Build assets into ${docRel}/build?`,
|
|
109
|
+
true,
|
|
110
|
+
);
|
|
111
|
+
if (ok) {
|
|
112
|
+
phpHost = php;
|
|
113
|
+
success("Production build will target the PHP doc-root (public/build)");
|
|
114
|
+
} else {
|
|
115
|
+
info("Keeping generic custom build (../dist)");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
// Moodle: detect frankenstyle component name (e.g. "local_middag", "mod_assign")
|
|
96
122
|
let moodleComponent = null;
|
|
97
123
|
if (hostKey === "moodle") {
|
|
@@ -249,7 +275,7 @@ if (isPro) {
|
|
|
249
275
|
|
|
250
276
|
// Host-specific production files (entry, vite config, theme CSS)
|
|
251
277
|
scaffoldHostEntry(targetDir, hostKey);
|
|
252
|
-
scaffoldHostViteConfig(targetDir, hostKey, host, withLicensing);
|
|
278
|
+
scaffoldHostViteConfig(targetDir, hostKey, host, withLicensing, phpHost);
|
|
253
279
|
scaffoldHostThemeCSS(targetDir, hostKey, host);
|
|
254
280
|
|
|
255
281
|
// Moodle-specific: AMD plugin + Moodle adapters (ajax, strings, notification)
|
package/lib/detect.js
CHANGED
|
@@ -113,3 +113,31 @@ export function detectHost(cwd) {
|
|
|
113
113
|
|
|
114
114
|
return null;
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Detect a PHP host and its doc-root, walking cwd + ancestors.
|
|
119
|
+
*
|
|
120
|
+
* Used to refine the 'custom' host: a PHP backend's production build must
|
|
121
|
+
* target the doc-root (public/build) with a proper base-href so lazy chunks
|
|
122
|
+
* resolve. PHP marker priority: composer.json (universal) → public/ doc-root;
|
|
123
|
+
* bin/console (Symfony) / artisan (Laravel) only refine the framework hint.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} cwd - Directory the scaffold runs in (ui/ goes under here)
|
|
126
|
+
* @returns {{ lang: 'php', framework: string|null, appRoot: string, docRoot: string|null } | null}
|
|
127
|
+
*/
|
|
128
|
+
export function detectPhpHost(cwd) {
|
|
129
|
+
let dir = cwd;
|
|
130
|
+
for (let i = 0; i <= MAX_DEPTH; i++) {
|
|
131
|
+
if (existsSync(join(dir, "composer.json"))) {
|
|
132
|
+
const docRoot = existsSync(join(dir, "public")) ? join(dir, "public") : null;
|
|
133
|
+
let framework = null;
|
|
134
|
+
if (existsSync(join(dir, "artisan"))) framework = "laravel";
|
|
135
|
+
else if (existsSync(join(dir, "bin", "console"))) framework = "symfony";
|
|
136
|
+
return { lang: "php", framework, appRoot: dir, docRoot };
|
|
137
|
+
}
|
|
138
|
+
const parent = dirname(dir);
|
|
139
|
+
if (parent === dir) break;
|
|
140
|
+
dir = parent;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
package/lib/scaffold.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { basename, dirname, join } from "node:path";
|
|
11
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { error, success, warn } from "./ui.js";
|
|
14
14
|
|
|
@@ -1435,7 +1435,7 @@ ${postMountCode}
|
|
|
1435
1435
|
* @param {object} host - HOSTS[hostKey] object
|
|
1436
1436
|
* @param {boolean} withLicensing - Whether to emit @middag-io/licensing delivery manifest plugin
|
|
1437
1437
|
*/
|
|
1438
|
-
export function scaffoldHostViteConfig(targetDir, hostKey, host, withLicensing = false) {
|
|
1438
|
+
export function scaffoldHostViteConfig(targetDir, hostKey, host, withLicensing = false, phpHost = null) {
|
|
1439
1439
|
const filePath = join(targetDir, `vite.config.${hostKey}.ts`);
|
|
1440
1440
|
const label = `vite.config.${hostKey}.ts`;
|
|
1441
1441
|
if (skipIfExists(filePath, label)) return;
|
|
@@ -1524,10 +1524,58 @@ export default defineConfig({
|
|
|
1524
1524
|
},
|
|
1525
1525
|
},
|
|
1526
1526
|
});
|
|
1527
|
+
`;
|
|
1528
|
+
writeFile(filePath, content, label);
|
|
1529
|
+
return;
|
|
1530
|
+
} else if (hostKey === "custom" && phpHost && phpHost.docRoot) {
|
|
1531
|
+
// PHP host: app-entry build into the doc-root. base:"/build/" scopes
|
|
1532
|
+
// lazy-chunk import URLs (heavy blocks: ChartPanel, FormPanel, DateField)
|
|
1533
|
+
// to where PHP serves /public/build — without it dynamic imports 404.
|
|
1534
|
+
// entry-custom.tsx self-executes (registerDefaults + createInertiaApp), so
|
|
1535
|
+
// this is an APP build (rollupOptions.input), NOT a library build.
|
|
1536
|
+
const docRootName = basename(phpHost.docRoot);
|
|
1537
|
+
const rel = relative(targetDir, join(phpHost.docRoot, "build")) || "../public/build";
|
|
1538
|
+
const outDirExpr = `resolve(__dirname, ${JSON.stringify(rel)})`;
|
|
1539
|
+
const content = `/**
|
|
1540
|
+
* Vite build config for the custom host (PHP) — production build target.
|
|
1541
|
+
*
|
|
1542
|
+
* Builds src/entry-custom.tsx as an APP entry (self-executes: registerDefaults
|
|
1543
|
+
* + createInertiaApp) so the bundle runs when the PHP shell loads
|
|
1544
|
+
* \`<script type="module" src="/build/app.js">\`. base:"/build/" scopes lazy
|
|
1545
|
+
* chunk import URLs to where the assets are served (${docRootName}/build).
|
|
1546
|
+
*
|
|
1547
|
+
* The dev server (\`npm run dev\`) uses vite.config.ts instead.
|
|
1548
|
+
*/
|
|
1549
|
+
import { defineConfig } from "vite";
|
|
1550
|
+
import react from "@vitejs/plugin-react";
|
|
1551
|
+
${licensingImport.trimEnd()}
|
|
1552
|
+
import { resolve } from "path";
|
|
1553
|
+
|
|
1554
|
+
export default defineConfig({
|
|
1555
|
+
plugins: [react()${licensingPluginEntry}],
|
|
1556
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
1557
|
+
resolve: { alias: { "@/": resolve(__dirname, "src") + "/" } },
|
|
1558
|
+
base: "/build/",
|
|
1559
|
+
build: {
|
|
1560
|
+
outDir: ${outDirExpr},
|
|
1561
|
+
emptyOutDir: true,
|
|
1562
|
+
cssCodeSplit: false,
|
|
1563
|
+
rollupOptions: {
|
|
1564
|
+
input: resolve(__dirname, "src/entry-custom.tsx"),
|
|
1565
|
+
output: {
|
|
1566
|
+
entryFileNames: "app.js",
|
|
1567
|
+
chunkFileNames: "[name]-[hash].js",
|
|
1568
|
+
assetFileNames: (assetInfo) =>
|
|
1569
|
+
assetInfo.name?.endsWith(".css") ? "style.css" : "[name]-[hash][extname]",
|
|
1570
|
+
},
|
|
1571
|
+
},
|
|
1572
|
+
},
|
|
1573
|
+
});
|
|
1527
1574
|
`;
|
|
1528
1575
|
writeFile(filePath, content, label);
|
|
1529
1576
|
return;
|
|
1530
1577
|
} else {
|
|
1578
|
+
// Generic custom (non-PHP) or PHP without a public/ doc-root — lib mode.
|
|
1531
1579
|
outDir = `resolve(__dirname, "../dist")`;
|
|
1532
1580
|
formats = `["es"]`;
|
|
1533
1581
|
libName = `"MiddagUI"`;
|
|
@@ -2002,7 +2050,7 @@ function readTemplate(relativePath) {
|
|
|
2002
2050
|
// ── Shared: register, page-resolver, route helper, demo page ────────────
|
|
2003
2051
|
|
|
2004
2052
|
/**
|
|
2005
|
-
* Scaffold FREE register: src/app/register.ts — the
|
|
2053
|
+
* Scaffold FREE register: src/app/register.ts — the 13 standard blocks.
|
|
2006
2054
|
*/
|
|
2007
2055
|
export function scaffoldFreeRegister(targetDir) {
|
|
2008
2056
|
ensureDir(join(targetDir, "src", "app"));
|
package/lib/scaffoldPRO.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Only available when installed from GitHub Packages.
|
|
6
6
|
*
|
|
7
7
|
* Generates the extended mock harness (mock/, src/app/register.ts)
|
|
8
|
-
* with 19 blocks (
|
|
8
|
+
* with 19 blocks (13 standard + 6 premium), extracted navigation/data/entities/routes files,
|
|
9
9
|
* and the slim app.tsx that delegates to mock/.
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -54,7 +54,7 @@ function skipIfExists(filePath, label) {
|
|
|
54
54
|
// ── 1. PRO register (19 blocks) ────────────────────────────────────────
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Scaffold `src/app/register.ts` — PRO version with 19 blocks (
|
|
57
|
+
* Scaffold `src/app/register.ts` — PRO version with 19 blocks (13 standard + 6 premium).
|
|
58
58
|
* Overrides any FREE register that may have been scaffolded.
|
|
59
59
|
*
|
|
60
60
|
* @param {string} targetDir - Absolute path to UI project root
|
|
@@ -12,8 +12,8 @@ import "./theme.css";
|
|
|
12
12
|
import "@fontsource-variable/figtree";
|
|
13
13
|
import { App } from "./app";
|
|
14
14
|
|
|
15
|
-
// Dev mode: register the free engine defaults (
|
|
16
|
-
// icons + cells) plus the premium runtime (the
|
|
15
|
+
// Dev mode: register the free engine defaults (13 standard blocks + fields +
|
|
16
|
+
// icons + cells) plus the premium runtime (the 6 heavy blocks). Then override
|
|
17
17
|
// the "product" shell with the host-sim MockProductShell inherited from
|
|
18
18
|
// @middag-io/react-demo (Moodle/WP chrome, host switcher, theme/locale toggles).
|
|
19
19
|
// Dev-only — in production, entry-*.tsx uses the selective register from ./app/register.
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* register — selective registration for this plugin's UI (PRO).
|
|
3
3
|
*
|
|
4
|
-
* Registers the
|
|
4
|
+
* Registers the 13 standard blocks (plus shells, layouts, cell renderers, form
|
|
5
5
|
* fields and icons) from @middag-io/react, then calls registerProDefaults()
|
|
6
6
|
* from @middag-io/react-pro to add the premium runtime: the rich ProductShell
|
|
7
|
-
* + chrome panels and the
|
|
8
|
-
*
|
|
7
|
+
* + chrome panels and the 6 interactive blocks (chart_panel, flow_editor,
|
|
8
|
+
* condition_tree, sentence_builder, form_builder, kanban_board).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Note: `form_panel` is a standard Community block (registered below); only
|
|
11
|
+
* `form_builder` is premium.
|
|
12
|
+
*
|
|
13
|
+
* Total: 19 blocks (13 standard + 6 premium).
|
|
11
14
|
*
|
|
12
15
|
* Full catalog: https://docs.middag.io/blocks
|
|
13
16
|
*/
|
|
@@ -26,7 +29,7 @@ import {
|
|
|
26
29
|
SidebarLayout,
|
|
27
30
|
DashboardLayout,
|
|
28
31
|
WizardLayout,
|
|
29
|
-
// Blocks (the
|
|
32
|
+
// Blocks (the 13 standard barrel exports)
|
|
30
33
|
DenseTableBlock,
|
|
31
34
|
MetricCardBlock,
|
|
32
35
|
EmptyStateBlock,
|
|
@@ -39,6 +42,7 @@ import {
|
|
|
39
42
|
CardGridBlock,
|
|
40
43
|
ActionGridBlock,
|
|
41
44
|
LinkListBlock,
|
|
45
|
+
FormPanelBlock,
|
|
42
46
|
} from "@middag-io/react";
|
|
43
47
|
import { registerProDefaults } from "@middag-io/react-pro/runtime";
|
|
44
48
|
|
|
@@ -57,7 +61,7 @@ export function registerDefaults(): void {
|
|
|
57
61
|
registerLayout("dashboard", DashboardLayout);
|
|
58
62
|
registerLayout("wizard", WizardLayout);
|
|
59
63
|
|
|
60
|
-
// Blocks — the
|
|
64
|
+
// Blocks — the 13 standard blocks from the barrel
|
|
61
65
|
registerBlock("dense_table", DenseTableBlock);
|
|
62
66
|
registerBlock("metric_card", MetricCardBlock);
|
|
63
67
|
registerBlock("empty_state", EmptyStateBlock);
|
|
@@ -70,6 +74,9 @@ export function registerDefaults(): void {
|
|
|
70
74
|
registerBlock("card_grid", CardGridBlock);
|
|
71
75
|
registerBlock("action_grid", ActionGridBlock);
|
|
72
76
|
registerBlock("link_list", LinkListBlock);
|
|
77
|
+
// form_panel pulls react-hook-form + zod (lazy-loaded); it is a standard
|
|
78
|
+
// Community block — drop it if this bundle has no forms.
|
|
79
|
+
registerBlock("form_panel", FormPanelBlock);
|
|
73
80
|
|
|
74
81
|
// Cell renderers (status, timestamp, link, boolean, etc.)
|
|
75
82
|
registerDefaultCells();
|
|
@@ -80,7 +87,7 @@ export function registerDefaults(): void {
|
|
|
80
87
|
// Icons (navigation, block, entity type icons)
|
|
81
88
|
registerDefaultIcons();
|
|
82
89
|
|
|
83
|
-
// Premium runtime — the rich ProductShell + chrome and the
|
|
90
|
+
// Premium runtime — the rich ProductShell + chrome and the 6 interactive
|
|
84
91
|
// blocks. Ships in @middag-io/react-pro (GitHub Packages, PRO tier).
|
|
85
92
|
registerProDefaults();
|
|
86
93
|
}
|