create-middag-ui 0.18.0 → 0.20.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 +16 -11
- package/lib/scaffold.js +44 -259
- package/lib/templates/pro/app.tsx +5 -1
- package/lib/templates/pro/main.tsx +11 -6
- package/lib/templates/pro/mock-routes.tsx +1 -1
- package/lib/templates/shared/register-free.ts +20 -10
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -30,11 +30,9 @@ import {
|
|
|
30
30
|
scaffoldDevShell,
|
|
31
31
|
scaffoldEslintConfig,
|
|
32
32
|
scaffoldGitignore,
|
|
33
|
-
scaffoldInjectPlaceholders,
|
|
34
33
|
scaffoldFreeAdapters,
|
|
35
34
|
scaffoldFreeApp,
|
|
36
35
|
scaffoldFreeRegister,
|
|
37
|
-
scaffoldProAdapters,
|
|
38
36
|
scaffoldHostEntry,
|
|
39
37
|
scaffoldHostThemeCSS,
|
|
40
38
|
scaffoldHostViteConfig,
|
|
@@ -171,12 +169,23 @@ if (!dirCreated) {
|
|
|
171
169
|
heading(5, TOTAL_STEPS, "Scaffolding config files");
|
|
172
170
|
|
|
173
171
|
const isPro = registryPath === "github";
|
|
174
|
-
|
|
172
|
+
|
|
173
|
+
// PRO only: @middag-io/licensing is a build-time delivery manifest, not a
|
|
174
|
+
// runtime gate. Not every PRO product needs it, so make it opt-in (default no).
|
|
175
|
+
const withLicensing =
|
|
176
|
+
isPro && !nonInteractive
|
|
177
|
+
? await confirm(
|
|
178
|
+
"Include @middag-io/licensing (build-time delivery manifest)?",
|
|
179
|
+
false,
|
|
180
|
+
)
|
|
181
|
+
: false;
|
|
182
|
+
|
|
183
|
+
scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey, withLicensing);
|
|
175
184
|
scaffoldTsconfig(targetDir);
|
|
176
185
|
scaffoldViteConfig(targetDir, host, registryPath);
|
|
177
|
-
scaffoldEslintConfig(targetDir
|
|
186
|
+
scaffoldEslintConfig(targetDir);
|
|
178
187
|
scaffoldPrettierConfig(targetDir);
|
|
179
|
-
scaffoldGitignore(targetDir
|
|
188
|
+
scaffoldGitignore(targetDir);
|
|
180
189
|
scaffoldIndexHtml(targetDir);
|
|
181
190
|
|
|
182
191
|
// ── Step 6: Scaffold ~/.npmrc (GitHub path only) ─────────────────────────
|
|
@@ -223,11 +232,7 @@ if (isPro) {
|
|
|
223
232
|
pro.scaffoldMockEntities(targetDir);
|
|
224
233
|
pro.scaffoldMockPageContracts(targetDir);
|
|
225
234
|
pro.scaffoldMockRoutes(targetDir);
|
|
226
|
-
|
|
227
|
-
// shares the same React context as MockPageProvider (no context mismatch).
|
|
228
|
-
scaffoldProAdapters(targetDir);
|
|
229
|
-
scaffoldInjectPlaceholders(targetDir);
|
|
230
|
-
success("PRO: using MockProductShell from @middag-io/react/mock");
|
|
235
|
+
success("PRO: host-sim dev harness inherited from @middag-io/react-demo");
|
|
231
236
|
} catch {
|
|
232
237
|
// npm version — PRO file excluded, fall back to FREE
|
|
233
238
|
info("PRO scaffold not available — using FREE path");
|
|
@@ -247,7 +252,7 @@ if (isPro) {
|
|
|
247
252
|
|
|
248
253
|
// Host-specific production files (entry, vite config, theme CSS)
|
|
249
254
|
scaffoldHostEntry(targetDir, hostKey);
|
|
250
|
-
scaffoldHostViteConfig(targetDir, hostKey, host);
|
|
255
|
+
scaffoldHostViteConfig(targetDir, hostKey, host, withLicensing);
|
|
251
256
|
scaffoldHostThemeCSS(targetDir, hostKey, host);
|
|
252
257
|
|
|
253
258
|
// Moodle-specific: AMD plugin + Moodle adapters (ajax, strings, notification)
|
package/lib/scaffold.js
CHANGED
|
@@ -91,7 +91,7 @@ export function createTargetDir(targetDir) {
|
|
|
91
91
|
* @param {string} registryPath - "github" (PRO) or "public" (FREE)
|
|
92
92
|
* @param {string} [hostKey] - 'wordpress' | 'moodle' | 'custom' (adds host build scripts)
|
|
93
93
|
*/
|
|
94
|
-
export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey) {
|
|
94
|
+
export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey, withLicensing = false) {
|
|
95
95
|
const filePath = join(targetDir, "package.json");
|
|
96
96
|
if (skipIfExists(filePath, "package.json")) return;
|
|
97
97
|
|
|
@@ -103,7 +103,12 @@ export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey)
|
|
|
103
103
|
"react-router": "^7.0.0",
|
|
104
104
|
};
|
|
105
105
|
if (isPro) {
|
|
106
|
+
deps["@middag-io/react-pro"] = `^${getLibVersion()}`;
|
|
106
107
|
deps["sonner"] = "^2.0.0";
|
|
108
|
+
// Opt-in: build-time delivery manifest, not every PRO product needs it.
|
|
109
|
+
if (withLicensing) {
|
|
110
|
+
deps["@middag-io/licensing"] = "^0.1.0";
|
|
111
|
+
}
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
// Moodle AMD build needs Tailwind Vite plugin for CSS processing
|
|
@@ -113,6 +118,13 @@ export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey)
|
|
|
113
118
|
moodleDevDeps["tailwindcss"] = "^4.0.0";
|
|
114
119
|
}
|
|
115
120
|
|
|
121
|
+
// PRO: dev harness (host-sim shell, Inertia mocks, i18n) inherited at dev time
|
|
122
|
+
// from @middag-io/react-demo. Dev-only — production entry-*.tsx never imports it.
|
|
123
|
+
const proDevDeps = {};
|
|
124
|
+
if (isPro) {
|
|
125
|
+
proDevDeps["@middag-io/react-demo"] = `^${getLibVersion()}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
116
128
|
const scripts = {
|
|
117
129
|
dev: "vite",
|
|
118
130
|
build: "vite build",
|
|
@@ -127,24 +139,12 @@ export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey)
|
|
|
127
139
|
if (hostKey === "wordpress") {
|
|
128
140
|
scripts["build:wp"] = "vite build --config vite.config.wordpress.ts";
|
|
129
141
|
scripts["watch:wp"] = "vite build --config vite.config.wordpress.ts --watch";
|
|
130
|
-
if (isPro) {
|
|
131
|
-
scripts["build:licensed"] =
|
|
132
|
-
"vite build --config vite.config.wordpress.ts && node scripts/inject-placeholders.mjs";
|
|
133
|
-
}
|
|
134
142
|
} else if (hostKey === "moodle") {
|
|
135
143
|
scripts["build:moodle"] = "vite build --config vite.config.moodle.ts";
|
|
136
144
|
scripts["watch:moodle"] = "vite build --config vite.config.moodle.ts --watch";
|
|
137
|
-
if (isPro) {
|
|
138
|
-
scripts["build:licensed"] =
|
|
139
|
-
"vite build --config vite.config.moodle.ts && node scripts/inject-placeholders.mjs";
|
|
140
|
-
}
|
|
141
145
|
} else if (hostKey === "custom") {
|
|
142
146
|
scripts["build:host"] = "vite build --config vite.config.custom.ts";
|
|
143
147
|
scripts["watch:host"] = "vite build --config vite.config.custom.ts --watch";
|
|
144
|
-
if (isPro) {
|
|
145
|
-
scripts["build:licensed"] =
|
|
146
|
-
"vite build --config vite.config.custom.ts && node scripts/inject-placeholders.mjs";
|
|
147
|
-
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
const pkg = {
|
|
@@ -173,6 +173,7 @@ export function scaffoldPackageJson(targetDir, host, cwd, registryPath, hostKey)
|
|
|
173
173
|
prettier: "^3.0.0",
|
|
174
174
|
"prettier-plugin-tailwindcss": "^0.6.0",
|
|
175
175
|
...moodleDevDeps,
|
|
176
|
+
...proDevDeps,
|
|
176
177
|
},
|
|
177
178
|
};
|
|
178
179
|
|
|
@@ -220,9 +221,19 @@ export function scaffoldViteConfig(targetDir, host, registryPath) {
|
|
|
220
221
|
const filePath = join(targetDir, "vite.config.ts");
|
|
221
222
|
if (skipIfExists(filePath, "vite.config.ts")) return;
|
|
222
223
|
|
|
223
|
-
const
|
|
224
|
-
? "
|
|
225
|
-
: "
|
|
224
|
+
const optimizeInclude = registryPath === "github"
|
|
225
|
+
? '["@middag-io/react", "@middag-io/react-pro", "@middag-io/react-demo"]'
|
|
226
|
+
: '["@middag-io/react"]';
|
|
227
|
+
|
|
228
|
+
// PRO inherits the dev harness (host-sim shell + Inertia mocks) from
|
|
229
|
+
// @middag-io/react-demo, so @inertiajs/* alias to it. FREE: local adapters.
|
|
230
|
+
const inertiaAlias = registryPath === "github"
|
|
231
|
+
? '// PRO: Inertia mocks inherited from @middag-io/react-demo (dev harness)\n' +
|
|
232
|
+
' "@inertiajs/react": "@middag-io/react-demo",\n' +
|
|
233
|
+
' "@inertiajs/core": "@middag-io/react-demo",'
|
|
234
|
+
: '// FREE: Inertia mocks from local self-contained adapters\n' +
|
|
235
|
+
' "@inertiajs/react": resolve(__dirname, "mock/adapters/inertia-react.ts"),\n' +
|
|
236
|
+
' "@inertiajs/core": resolve(__dirname, "mock/adapters/inertia-core.ts"),';
|
|
226
237
|
|
|
227
238
|
const content = `/**
|
|
228
239
|
* Vite config \u2014 used by \`npm run dev\` and \`npm run build\`.
|
|
@@ -239,15 +250,13 @@ export default defineConfig({
|
|
|
239
250
|
plugins: [react()],
|
|
240
251
|
server: { port: ${host.port} },
|
|
241
252
|
optimizeDeps: {
|
|
242
|
-
include:
|
|
253
|
+
include: ${optimizeInclude},
|
|
243
254
|
},
|
|
244
255
|
resolve: {
|
|
245
256
|
alias: {
|
|
246
257
|
"@/": resolve(__dirname, "src") + "/",
|
|
247
258
|
"@mock/": resolve(__dirname, "mock") + "/",
|
|
248
|
-
${
|
|
249
|
-
"@inertiajs/react": resolve(__dirname, "mock/adapters/inertia-react.ts"),
|
|
250
|
-
"@inertiajs/core": resolve(__dirname, "mock/adapters/inertia-core.ts"),
|
|
259
|
+
${inertiaAlias}
|
|
251
260
|
},
|
|
252
261
|
},
|
|
253
262
|
});
|
|
@@ -259,11 +268,11 @@ export default defineConfig({
|
|
|
259
268
|
/**
|
|
260
269
|
* Scaffold eslint.config.js.
|
|
261
270
|
*/
|
|
262
|
-
export function scaffoldEslintConfig(targetDir
|
|
271
|
+
export function scaffoldEslintConfig(targetDir) {
|
|
263
272
|
const filePath = join(targetDir, "eslint.config.js");
|
|
264
273
|
if (skipIfExists(filePath, "eslint.config.js")) return;
|
|
265
274
|
|
|
266
|
-
const ignores = ["dist/", "node_modules/", "scripts/"
|
|
275
|
+
const ignores = ["dist/", "node_modules/", "scripts/"];
|
|
267
276
|
const ignoresStr = ignores.map((i) => `"${i}"`).join(", ");
|
|
268
277
|
|
|
269
278
|
writeFile(
|
|
@@ -328,14 +337,13 @@ export function scaffoldPrettierConfig(targetDir) {
|
|
|
328
337
|
/**
|
|
329
338
|
* Scaffold .gitignore with UI-specific dist patterns.
|
|
330
339
|
*/
|
|
331
|
-
export function scaffoldGitignore(targetDir
|
|
340
|
+
export function scaffoldGitignore(targetDir) {
|
|
332
341
|
const filePath = join(targetDir, ".gitignore");
|
|
333
342
|
if (skipIfExists(filePath, ".gitignore")) return;
|
|
334
343
|
|
|
335
344
|
const lines = [
|
|
336
345
|
"node_modules/",
|
|
337
346
|
"dist/",
|
|
338
|
-
...(isPro ? ["dist-licensed/"] : []),
|
|
339
347
|
"dist-master/",
|
|
340
348
|
"dist-mock/",
|
|
341
349
|
"dist-lib/",
|
|
@@ -345,79 +353,6 @@ export function scaffoldGitignore(targetDir, isPro = false) {
|
|
|
345
353
|
writeFile(filePath, lines.join("\n") + "\n", ".gitignore");
|
|
346
354
|
}
|
|
347
355
|
|
|
348
|
-
/**
|
|
349
|
-
* Scaffold scripts/inject-placeholders.mjs (PRO only).
|
|
350
|
-
* Injects opaque placeholder variables into middag-* bundles for CDN delivery.
|
|
351
|
-
* The Cloudflare Worker replaces placeholders with per-installation values.
|
|
352
|
-
*/
|
|
353
|
-
export function scaffoldInjectPlaceholders(targetDir) {
|
|
354
|
-
const scriptsDir = join(targetDir, "scripts");
|
|
355
|
-
mkdirSync(scriptsDir, { recursive: true });
|
|
356
|
-
const filePath = join(scriptsDir, "inject-placeholders.mjs");
|
|
357
|
-
if (skipIfExists(filePath, "scripts/inject-placeholders.mjs")) return;
|
|
358
|
-
|
|
359
|
-
writeFile(
|
|
360
|
-
filePath,
|
|
361
|
-
`/**
|
|
362
|
-
* inject-placeholders — generate licensed bundles for CDN delivery.
|
|
363
|
-
*
|
|
364
|
-
* Takes compiled Vite output from dist/ and produces dist-licensed/ with
|
|
365
|
-
* placeholder variables injected into middag-* bundles. The Cloudflare
|
|
366
|
-
* Worker replaces these placeholders with real values per installation.
|
|
367
|
-
*
|
|
368
|
-
* Usage: node scripts/inject-placeholders.mjs
|
|
369
|
-
* Called by: npm run build:licensed
|
|
370
|
-
*/
|
|
371
|
-
import fs from "node:fs";
|
|
372
|
-
import path from "node:path";
|
|
373
|
-
|
|
374
|
-
const DIST_DIR = path.resolve(import.meta.dirname, "../dist");
|
|
375
|
-
const LICENSED_DIR = path.resolve(import.meta.dirname, "../dist-licensed");
|
|
376
|
-
|
|
377
|
-
// Variable names are intentionally opaque to discourage reverse engineering.
|
|
378
|
-
// The mapping is documented only here and in the Worker source:
|
|
379
|
-
// _0x7a3f → wwwroot (replaced by Worker with installation URL)
|
|
380
|
-
// _0x9b1e → expiry (replaced by Worker with unix timestamp)
|
|
381
|
-
// _0x4d2c → tier (replaced by Worker with license tier)
|
|
382
|
-
// _0x6e8a → domain hash (replaced by Worker with HMAC of wwwroot)
|
|
383
|
-
const PLACEHOLDER_HEADER = [
|
|
384
|
-
'var _0x7a3f="__PH_0x7a3f__";',
|
|
385
|
-
"var _0x9b1e=0;",
|
|
386
|
-
'var _0x4d2c="__PH_0x4d2c__";',
|
|
387
|
-
'var _0x6e8a="__PH_0x6e8a__";',
|
|
388
|
-
].join("") + "\\n";
|
|
389
|
-
|
|
390
|
-
if (fs.existsSync(LICENSED_DIR)) fs.rmSync(LICENSED_DIR, { recursive: true });
|
|
391
|
-
fs.mkdirSync(LICENSED_DIR, { recursive: true });
|
|
392
|
-
|
|
393
|
-
const files = fs.readdirSync(DIST_DIR).filter((f) => f.endsWith(".js"));
|
|
394
|
-
let injected = 0;
|
|
395
|
-
let copied = 0;
|
|
396
|
-
|
|
397
|
-
for (const file of files) {
|
|
398
|
-
const srcPath = path.join(DIST_DIR, file);
|
|
399
|
-
const destPath = path.join(LICENSED_DIR, file);
|
|
400
|
-
const content = fs.readFileSync(srcPath, "utf-8");
|
|
401
|
-
|
|
402
|
-
if (file.startsWith("middag-")) {
|
|
403
|
-
fs.writeFileSync(destPath, PLACEHOLDER_HEADER + content);
|
|
404
|
-
injected++;
|
|
405
|
-
console.log(\`[licensed] \${file} — placeholders injected\`);
|
|
406
|
-
} else {
|
|
407
|
-
fs.copyFileSync(srcPath, destPath);
|
|
408
|
-
copied++;
|
|
409
|
-
console.log(\`[licensed] \${file} — copied (vendor/shared)\`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
console.log(\`\\nLicensed bundles ready in \${LICENSED_DIR}/\`);
|
|
414
|
-
console.log(\` \${injected} bundle(s) with placeholders\`);
|
|
415
|
-
console.log(\` \${copied} bundle(s) copied as-is\`);
|
|
416
|
-
`,
|
|
417
|
-
"scripts/inject-placeholders.mjs",
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
356
|
/**
|
|
422
357
|
* Scaffold index.html at project root.
|
|
423
358
|
*/
|
|
@@ -1043,132 +978,6 @@ export const settingsContract: PageContract = {
|
|
|
1043
978
|
}
|
|
1044
979
|
}
|
|
1045
980
|
|
|
1046
|
-
// ── App files — PRO path (GitHub Packages) ─────────────────────────────
|
|
1047
|
-
|
|
1048
|
-
/**
|
|
1049
|
-
* Scaffold PRO app: src/main.tsx + src/app.tsx.
|
|
1050
|
-
* Uses mock barrel from @middag-io/react/mock. No local adapters/shell.
|
|
1051
|
-
*/
|
|
1052
|
-
export function scaffoldProApp(targetDir) {
|
|
1053
|
-
ensureDir(join(targetDir, "src"));
|
|
1054
|
-
ensureDir(join(targetDir, "mock", "adapters"));
|
|
1055
|
-
|
|
1056
|
-
// Inertia adapter shims — re-export from pre-built mock bundle
|
|
1057
|
-
// so usePage() shares the same MockPageContext as MockPageProvider.
|
|
1058
|
-
const adapterReactPath = join(targetDir, "mock", "adapters", "inertia-react.ts");
|
|
1059
|
-
if (!skipIfExists(adapterReactPath, "mock/adapters/inertia-react.ts")) {
|
|
1060
|
-
writeFile(adapterReactPath, `/**
|
|
1061
|
-
* Mock @inertiajs/react — re-exports from pre-built @middag-io/react/mock.
|
|
1062
|
-
*
|
|
1063
|
-
* This ensures usePage() reads from the same MockPageContext as
|
|
1064
|
-
* MockPageProvider (both live in the pre-built ESM bundle).
|
|
1065
|
-
*/
|
|
1066
|
-
export { usePage, Head, Link, router } from "@middag-io/react/mock";
|
|
1067
|
-
`, "mock/adapters/inertia-react.ts");
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const adapterCorePath = join(targetDir, "mock", "adapters", "inertia-core.ts");
|
|
1071
|
-
if (!skipIfExists(adapterCorePath, "mock/adapters/inertia-core.ts")) {
|
|
1072
|
-
writeFile(adapterCorePath, `/**
|
|
1073
|
-
* Mock @inertiajs/core — re-exports from pre-built @middag-io/react/mock.
|
|
1074
|
-
*/
|
|
1075
|
-
export { router, setMockNavigate } from "@middag-io/react/mock";
|
|
1076
|
-
`, "mock/adapters/inertia-core.ts");
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
const mainPath = join(targetDir, "src", "main.tsx");
|
|
1080
|
-
if (!skipIfExists(mainPath, "src/main.tsx")) {
|
|
1081
|
-
writeFile(mainPath, `import { StrictMode } from "react";
|
|
1082
|
-
import { createRoot } from "react-dom/client";
|
|
1083
|
-
import { registerDefaults, registerShell } from "@middag-io/react";
|
|
1084
|
-
import { MockProductShell } from "@middag-io/react/mock";
|
|
1085
|
-
import "@middag-io/react/style.css";
|
|
1086
|
-
import "./theme.css";
|
|
1087
|
-
import "@fontsource-variable/figtree";
|
|
1088
|
-
import { App } from "./app";
|
|
1089
|
-
|
|
1090
|
-
registerDefaults();
|
|
1091
|
-
registerShell("product", MockProductShell);
|
|
1092
|
-
|
|
1093
|
-
createRoot(document.getElementById("root")!).render(
|
|
1094
|
-
<StrictMode><App /></StrictMode>,
|
|
1095
|
-
);
|
|
1096
|
-
`, "src/main.tsx");
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
const appPath = join(targetDir, "src", "app.tsx");
|
|
1100
|
-
if (!skipIfExists(appPath, "src/app.tsx")) {
|
|
1101
|
-
writeFile(appPath, `import { useEffect } from "react";
|
|
1102
|
-
import { BrowserRouter, Routes, Route, useNavigate } from "react-router";
|
|
1103
|
-
import { ContractPage, I18nProvider } from "@middag-io/react";
|
|
1104
|
-
import { MockPageProvider, MockI18nProvider } from "@middag-io/react/mock";
|
|
1105
|
-
import type { PageContract } from "@middag-io/react";
|
|
1106
|
-
import { dashboardContract } from "./pages/dashboard";
|
|
1107
|
-
import { connectorsContract } from "./pages/connectors";
|
|
1108
|
-
import { settingsContract } from "./pages/settings";
|
|
1109
|
-
|
|
1110
|
-
let _navigate: ((to: string) => void) | null = null;
|
|
1111
|
-
function NavigateBridge() {
|
|
1112
|
-
const navigate = useNavigate();
|
|
1113
|
-
useEffect(() => { _navigate = (to: string) => navigate(to); }, [navigate]);
|
|
1114
|
-
return null;
|
|
1115
|
-
}
|
|
1116
|
-
if (typeof window !== "undefined") {
|
|
1117
|
-
(window as any).__MIDDAG_MOCK_NAVIGATE__ = (to: string) => { if (_navigate) _navigate(to); };
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
const sharedProps = {
|
|
1121
|
-
auth: { id: 1, name: "Dev User", email: "dev@localhost", capabilities: [] },
|
|
1122
|
-
theme: { appearance: "light" as const, strings: {} as Record<string, string> },
|
|
1123
|
-
flash: {},
|
|
1124
|
-
locale: "en",
|
|
1125
|
-
version: "0.0.0-dev",
|
|
1126
|
-
scope: { extension: null, context: "global" },
|
|
1127
|
-
};
|
|
1128
|
-
|
|
1129
|
-
function buildNavigation(activeKey: string) {
|
|
1130
|
-
return {
|
|
1131
|
-
tree: [
|
|
1132
|
-
{ key: "overview.dashboard", label: "Dashboard", icon: "home", href: "/", children: [] },
|
|
1133
|
-
{ key: "integration.connectors", label: "Connectors", icon: "plug", href: "/connectors", children: [] },
|
|
1134
|
-
],
|
|
1135
|
-
footer: [
|
|
1136
|
-
{ key: "system.settings", label: "Settings", icon: "settings", href: "/settings", children: [] },
|
|
1137
|
-
],
|
|
1138
|
-
activeKey,
|
|
1139
|
-
};
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
function MockRoute({ contract, activeKey }: { contract: PageContract; activeKey: string }) {
|
|
1143
|
-
return (
|
|
1144
|
-
<MockPageProvider value={{ props: { ...sharedProps, contract, navigation: buildNavigation(activeKey) }, url: window.location.pathname }}>
|
|
1145
|
-
<ContractPage contract={contract} />
|
|
1146
|
-
</MockPageProvider>
|
|
1147
|
-
);
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
export function App() {
|
|
1151
|
-
return (
|
|
1152
|
-
<MockI18nProvider>
|
|
1153
|
-
<MockPageProvider value={{ props: { ...sharedProps, contract: null, navigation: buildNavigation("") }, url: "/" }}>
|
|
1154
|
-
<I18nProvider>
|
|
1155
|
-
<BrowserRouter>
|
|
1156
|
-
<NavigateBridge />
|
|
1157
|
-
<Routes>
|
|
1158
|
-
<Route path="/" element={<MockRoute contract={dashboardContract} activeKey="overview.dashboard" />} />
|
|
1159
|
-
<Route path="/connectors" element={<MockRoute contract={connectorsContract} activeKey="integration.connectors" />} />
|
|
1160
|
-
<Route path="/settings" element={<MockRoute contract={settingsContract} activeKey="system.settings" />} />
|
|
1161
|
-
</Routes>
|
|
1162
|
-
</BrowserRouter>
|
|
1163
|
-
</I18nProvider>
|
|
1164
|
-
</MockPageProvider>
|
|
1165
|
-
</MockI18nProvider>
|
|
1166
|
-
);
|
|
1167
|
-
}
|
|
1168
|
-
`, "src/app.tsx");
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
981
|
/**
|
|
1173
982
|
* Scaffold FREE adapters: mock/adapters/ with react-router.
|
|
1174
983
|
*/
|
|
@@ -1248,39 +1057,6 @@ export { router };
|
|
|
1248
1057
|
}
|
|
1249
1058
|
}
|
|
1250
1059
|
|
|
1251
|
-
/**
|
|
1252
|
-
* Scaffold PRO adapters: thin re-exports from @middag-io/react/mock.
|
|
1253
|
-
*
|
|
1254
|
-
* PRO uses MockPageProvider from the lib, so usePage() must read from the
|
|
1255
|
-
* same React context. These adapters delegate to the lib mock instead of
|
|
1256
|
-
* defining their own context (which would cause context mismatch).
|
|
1257
|
-
*/
|
|
1258
|
-
export function scaffoldProAdapters(targetDir) {
|
|
1259
|
-
ensureDir(join(targetDir, "mock", "adapters"));
|
|
1260
|
-
|
|
1261
|
-
const corePath = join(targetDir, "mock", "adapters", "inertia-core.ts");
|
|
1262
|
-
if (!skipIfExists(corePath, "mock/adapters/inertia-core.ts")) {
|
|
1263
|
-
writeFile(corePath, `/**
|
|
1264
|
-
* Mock @inertiajs/core — PRO re-export from @middag-io/react/mock.
|
|
1265
|
-
* Shares the same navigate function as MockProductShell.
|
|
1266
|
-
*/
|
|
1267
|
-
export { router, setMockNavigate } from "@middag-io/react/mock";
|
|
1268
|
-
`, "mock/adapters/inertia-core.ts (PRO)");
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
const reactPath = join(targetDir, "mock", "adapters", "inertia-react.ts");
|
|
1272
|
-
if (!skipIfExists(reactPath, "mock/adapters/inertia-react.ts")) {
|
|
1273
|
-
writeFile(reactPath, `/**
|
|
1274
|
-
* Mock @inertiajs/react — PRO re-export from @middag-io/react/mock.
|
|
1275
|
-
* Shares the same React context as MockPageProvider so usePage()
|
|
1276
|
-
* returns the data set by the mock shell.
|
|
1277
|
-
*/
|
|
1278
|
-
export { usePage, Head, Link } from "@middag-io/react/mock";
|
|
1279
|
-
export { router } from "./inertia-core";
|
|
1280
|
-
`, "mock/adapters/inertia-react.ts (PRO)");
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
1060
|
/**
|
|
1285
1061
|
* Scaffold FREE DevShell: src/shells/DevShell.tsx.
|
|
1286
1062
|
*/
|
|
@@ -1622,13 +1398,20 @@ ${postMountCode}
|
|
|
1622
1398
|
* @param {string} targetDir - Absolute path to UI dir
|
|
1623
1399
|
* @param {string} hostKey - 'wordpress' | 'moodle' | 'custom'
|
|
1624
1400
|
* @param {object} host - HOSTS[hostKey] object
|
|
1401
|
+
* @param {boolean} withLicensing - Whether to emit @middag-io/licensing delivery manifest plugin
|
|
1625
1402
|
*/
|
|
1626
|
-
export function scaffoldHostViteConfig(targetDir, hostKey, host) {
|
|
1403
|
+
export function scaffoldHostViteConfig(targetDir, hostKey, host, withLicensing = false) {
|
|
1627
1404
|
const filePath = join(targetDir, `vite.config.${hostKey}.ts`);
|
|
1628
1405
|
const label = `vite.config.${hostKey}.ts`;
|
|
1629
1406
|
if (skipIfExists(filePath, label)) return;
|
|
1630
1407
|
|
|
1631
1408
|
let outDir, formats, libName, fileName, extraRollup;
|
|
1409
|
+
const licensingImport = withLicensing
|
|
1410
|
+
? `import { licensingPlugin } from "@middag-io/licensing/build";\n`
|
|
1411
|
+
: "";
|
|
1412
|
+
const licensingPluginEntry = withLicensing
|
|
1413
|
+
? `,\n licensingPlugin({\n product: process.env.MIDDAG_PRODUCT ?? "middag-ui",\n release: process.env.MIDDAG_RELEASE ?? "local",\n buildId: process.env.MIDDAG_BUILD_ID ?? "local",\n modules: [],\n })`
|
|
1414
|
+
: "";
|
|
1632
1415
|
|
|
1633
1416
|
if (hostKey === "wordpress") {
|
|
1634
1417
|
outDir = `resolve(__dirname, "../assets/dist")`;
|
|
@@ -1662,10 +1445,11 @@ import { defineConfig } from "vite";
|
|
|
1662
1445
|
import react from "@vitejs/plugin-react";
|
|
1663
1446
|
import tailwindcss from "@tailwindcss/vite";
|
|
1664
1447
|
import moodleAmd from "./plugins/vite-plugin-moodle-amd";
|
|
1448
|
+
${licensingImport.trimEnd()}
|
|
1665
1449
|
import { resolve } from "path";
|
|
1666
1450
|
|
|
1667
1451
|
export default defineConfig({
|
|
1668
|
-
plugins: [react(), tailwindcss(), moodleAmd()],
|
|
1452
|
+
plugins: [react(), tailwindcss(), moodleAmd()${licensingPluginEntry}],
|
|
1669
1453
|
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
1670
1454
|
resolve: { alias: { "@/": resolve(__dirname, "src") + "/" } },
|
|
1671
1455
|
build: {
|
|
@@ -1734,10 +1518,11 @@ export default defineConfig({
|
|
|
1734
1518
|
*/
|
|
1735
1519
|
import { defineConfig } from "vite";
|
|
1736
1520
|
import react from "@vitejs/plugin-react";
|
|
1521
|
+
${licensingImport.trimEnd()}
|
|
1737
1522
|
import { resolve } from "path";
|
|
1738
1523
|
|
|
1739
1524
|
export default defineConfig({
|
|
1740
|
-
plugins: [react()],
|
|
1525
|
+
plugins: [react()${licensingPluginEntry}],
|
|
1741
1526
|
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
1742
1527
|
resolve: { alias: { "@/": resolve(__dirname, "src") + "/" } },
|
|
1743
1528
|
build: {
|
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
* mock/routes.tsx — route definitions
|
|
6
6
|
* mock/navigation.ts — sidebar structure
|
|
7
7
|
* mock/data.ts — synthetic page props
|
|
8
|
+
*
|
|
9
|
+
* The dev harness (host-sim shell, Inertia mocks, i18n) is inherited from
|
|
10
|
+
* @middag-io/react-demo — not generated locally. Vite aliases @inertiajs/*
|
|
11
|
+
* to it (see vite.config.ts).
|
|
8
12
|
*/
|
|
9
13
|
import {useEffect} from "react";
|
|
10
14
|
import {BrowserRouter, Routes, useNavigate} from "react-router";
|
|
11
15
|
import {I18nProvider} from "@middag-io/react";
|
|
12
|
-
import {MockI18nProvider, MockPageProvider, setMockNavigate} from "@middag-io/react
|
|
16
|
+
import {MockI18nProvider, MockPageProvider, setMockNavigate} from "@middag-io/react-demo";
|
|
13
17
|
import {buildNavigation} from "../mock/navigation";
|
|
14
18
|
import {sharedProps} from "../mock/data";
|
|
15
19
|
import {AppRoutes} from "../mock/routes";
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { StrictMode } from "react";
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
3
|
import { registerDefaults, registerShell } from "@middag-io/react";
|
|
4
|
-
import {
|
|
4
|
+
import { registerProDefaults } from "@middag-io/react-pro/runtime";
|
|
5
|
+
import { MockProductShell } from "@middag-io/react-demo";
|
|
5
6
|
import "@middag-io/react/style.css";
|
|
6
7
|
import "@middag-io/react/themes/classic.css";
|
|
7
|
-
import "@middag-io/react/themes/enterprise.css";
|
|
8
|
-
import "@middag-io/react/themes/soft.css";
|
|
9
|
-
import "@middag-io/react/themes/midnight.css";
|
|
8
|
+
import "@middag-io/react-pro/themes/enterprise.css";
|
|
9
|
+
import "@middag-io/react-pro/themes/soft.css";
|
|
10
|
+
import "@middag-io/react-pro/themes/midnight.css";
|
|
10
11
|
import "./theme.css";
|
|
11
12
|
import "@fontsource-variable/figtree";
|
|
12
13
|
import { App } from "./app";
|
|
13
14
|
|
|
14
|
-
// Dev mode: register
|
|
15
|
-
//
|
|
15
|
+
// Dev mode: register the free engine defaults (12 standard blocks + fields +
|
|
16
|
+
// icons + cells) plus the premium runtime (the 7 heavy blocks). Then override
|
|
17
|
+
// the "product" shell with the host-sim MockProductShell inherited from
|
|
18
|
+
// @middag-io/react-demo (Moodle/WP chrome, host switcher, theme/locale toggles).
|
|
19
|
+
// Dev-only — in production, entry-*.tsx uses the selective register from ./app/register.
|
|
16
20
|
registerDefaults();
|
|
21
|
+
registerProDefaults();
|
|
17
22
|
registerShell("product", MockProductShell);
|
|
18
23
|
|
|
19
24
|
createRoot(document.getElementById("root")!).render(
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { Route } from "react-router";
|
|
7
7
|
import { type ReactNode } from "react";
|
|
8
8
|
import { ContractPage, resolveShell, EntityRoutesProvider } from "@middag-io/react";
|
|
9
|
-
import { MockPageProvider } from "@middag-io/react
|
|
9
|
+
import { MockPageProvider } from "@middag-io/react-demo";
|
|
10
10
|
import type { PageContract } from "@middag-io/react";
|
|
11
11
|
import { mockEntities } from "./entities";
|
|
12
12
|
import { buildNavigation } from "./navigation";
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* register —
|
|
2
|
+
* register — registration for this plugin's UI.
|
|
3
3
|
*
|
|
4
|
-
* Registers
|
|
5
|
-
* and icons
|
|
6
|
-
*
|
|
4
|
+
* Registers all 13 standard blocks plus shells, layouts, cell renderers, form
|
|
5
|
+
* fields and icons. Every block in @middag-io/react is free to use — there is no
|
|
6
|
+
* tiered block gating. The 6 heavy lazy-loaded blocks (chart_panel, kanban_board,
|
|
7
|
+
* flow_editor, form_builder, condition_tree, sentence_builder) are NOT registered
|
|
8
|
+
* here to keep the bundle lean; deep-import + registerBlock() them when a page
|
|
9
|
+
* needs one, or call registerDefaults() from @middag-io/react to register all 19.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
* add the import + registerBlock call.
|
|
11
|
+
* For lean IIFE bundles (WordPress/Moodle), trim the blocks you don't use.
|
|
10
12
|
*
|
|
11
13
|
* Full catalog: https://docs.middag.io/blocks
|
|
12
14
|
*/
|
|
@@ -31,10 +33,14 @@ import {
|
|
|
31
33
|
MetricCardBlock,
|
|
32
34
|
EmptyStateBlock,
|
|
33
35
|
DetailPanelBlock,
|
|
34
|
-
FormPanelBlock,
|
|
35
|
-
CardGridBlock,
|
|
36
36
|
StatusStripBlock,
|
|
37
|
+
FormPanelBlock,
|
|
37
38
|
TabbedPanelBlock,
|
|
39
|
+
ActivityTimelineBlock,
|
|
40
|
+
WorkflowProgressBlock,
|
|
41
|
+
MarkdownPanelBlock,
|
|
42
|
+
CardGridBlock,
|
|
43
|
+
ActionGridBlock,
|
|
38
44
|
LinkListBlock,
|
|
39
45
|
} from "@middag-io/react";
|
|
40
46
|
|
|
@@ -59,10 +65,14 @@ export function registerDefaults(): void {
|
|
|
59
65
|
registerBlock("metric_card", MetricCardBlock);
|
|
60
66
|
registerBlock("empty_state", EmptyStateBlock);
|
|
61
67
|
registerBlock("detail_panel", DetailPanelBlock);
|
|
62
|
-
registerBlock("form_panel", FormPanelBlock);
|
|
63
|
-
registerBlock("card_grid", CardGridBlock);
|
|
64
68
|
registerBlock("status_strip", StatusStripBlock);
|
|
69
|
+
registerBlock("form_panel", FormPanelBlock);
|
|
65
70
|
registerBlock("tabbed_panel", TabbedPanelBlock);
|
|
71
|
+
registerBlock("activity_timeline", ActivityTimelineBlock);
|
|
72
|
+
registerBlock("workflow_progress", WorkflowProgressBlock);
|
|
73
|
+
registerBlock("markdown_panel", MarkdownPanelBlock);
|
|
74
|
+
registerBlock("card_grid", CardGridBlock);
|
|
75
|
+
registerBlock("action_grid", ActionGridBlock);
|
|
66
76
|
registerBlock("link_list", LinkListBlock);
|
|
67
77
|
|
|
68
78
|
// Cell renderers (status, timestamp, link, boolean, etc.)
|