@multisystemsuite/create-mf-app 1.0.14 → 1.0.16

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 (3) hide show
  1. package/README.md +18 -0
  2. package/dist/index.js +106 -47
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -303,9 +303,25 @@ npx create-mf-app <project-name> [options]
303
303
  | `--federation=true\|false` | Module Federation (default: `true`) |
304
304
  | `--control-plane` | Workflow Designer & Monitoring preset (6 apps + corelib, control-plane API, mf-* packages) |
305
305
  | `--federation-os` | Full Enterprise Federation OS preset (10 apps + corelib, runtime, observability stack) |
306
+ | `--lite` | **Faster install** — skip ESLint/Vitest/Husky stack and optional SDK packages |
306
307
  | `-y`, `--yes` | Non-interactive (use defaults where applicable) |
307
308
  | `-h`, `--help` | Show help |
308
309
 
310
+ **Faster install (`--lite`):**
311
+
312
+ ```bash
313
+ npx create-mf-app my-platform --type=parent --children=billing,orders --shared=corelib --lite -y
314
+ ```
315
+
316
+ Skips ESLint, Prettier, Stylelint, Vitest, Husky, commitlint, and optional SDK packages (`api-client`, `sdk-core`, `websocket-sdk`). Federation, apps, `corelib`, and `query-client` are still included — typical install drops from ~850 to ~400 packages.
317
+
318
+ **Other ways to speed up install:**
319
+
320
+ - Exclude the project folder from Windows Defender real-time scan
321
+ - Second `npm install` is much faster (npm cache)
322
+ - Use `HUSKY=0 npm install` to skip git hooks on any scaffold
323
+ - Fewer `--children` = fewer apps = less to resolve
324
+
309
325
  ### Add a child to an existing parent workspace
310
326
 
311
327
  ```bash
@@ -1431,6 +1447,8 @@ Node 18 will fail on Vite 7 commands.
1431
1447
  | `Cannot find module 'billing/App'` | Add federation `.d.ts` declarations in host |
1432
1448
  | `Task not found: live-reload` | Root `"live-reload": "node live-reload.js"` |
1433
1449
  | Vite binary missing after install | Clean reinstall (see below) |
1450
+ | **`npm install` very slow (~2 min+)** | Use `--lite` for faster scaffold; exclude project from antivirus scan; second install uses npm cache |
1451
+ | **`npm run local` fails on `api-client` build** | Update to latest CLI (smart-build skips source-only packages) or patch `smart-build.js` |
1434
1452
  | Sidebar overlaps main content | Use generated flex sidebar in corelib |
1435
1453
  | **Text invisible on dark header/sidebar** | Clear `localStorage` key `mf-enterprise-config`; hard refresh |
1436
1454
  | `provider support react(undefined)` / `useState` null | Rebuild all apps after federation config change; host uses exact `version: "19.0.0"`, remotes use `requiredVersion: "^19.0.0"` |
package/dist/index.js CHANGED
@@ -365,6 +365,17 @@ function writeCache(projectId, hash) {
365
365
  fs.writeFileSync(cachePath(projectId), JSON.stringify({ hash, mode, at: Date.now() }, null, 2));
366
366
  }
367
367
 
368
+ function hasBuildScript(projectRoot) {
369
+ const pkgPath = path.join(projectRoot, "package.json");
370
+ if (!fs.existsSync(pkgPath)) return false;
371
+ try {
372
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
373
+ return typeof pkg.scripts?.build === "string" && pkg.scripts.build.trim().length > 0;
374
+ } catch {
375
+ return false;
376
+ }
377
+ }
378
+
368
379
  function runBuild(projectId, root) {
369
380
  return new Promise((resolve) => {
370
381
  const npm = process.platform === "win32" ? "npm.cmd" : "npm";
@@ -392,6 +403,12 @@ async function main() {
392
403
  let order = topoSort(graph, onlyProjects);
393
404
  if (!order.length) order = Object.keys(config.projects ?? {});
394
405
 
406
+ order = order.filter((projectId) => {
407
+ const meta = config.projects[projectId];
408
+ if (!meta?.root) return false;
409
+ return hasBuildScript(path.join(cwd, meta.root));
410
+ });
411
+
395
412
  console.log("[smart-build] mode=" + mode + " projects=" + order.join(", "));
396
413
 
397
414
  const waves = [];
@@ -414,6 +431,10 @@ async function main() {
414
431
  const meta = config.projects[projectId];
415
432
  if (!meta?.root) return;
416
433
  const projectRoot = path.join(cwd, meta.root);
434
+ if (!hasBuildScript(projectRoot)) {
435
+ console.log("[skip] " + projectId + " (no build script \u2014 source-only workspace package)");
436
+ return;
437
+ }
417
438
  const hash = hashProject(projectRoot);
418
439
  if (skipUnchanged) {
419
440
  const cached = readCache(projectId);
@@ -3521,28 +3542,12 @@ function mergeAppTsconfigForQuality(existing) {
3521
3542
  `;
3522
3543
  }
3523
3544
  var APP_QUALITY_DEV_DEPS = {
3524
- vitest: "^3.0.5",
3525
- "@vitest/coverage-v8": "^3.0.5",
3526
- jsdom: "^25.0.1",
3527
- "@testing-library/react": "^16.2.0",
3528
- "@testing-library/jest-dom": "^6.6.3",
3529
- "@testing-library/user-event": "^14.5.2",
3530
3545
  "cross-env": "^7.0.3",
3531
3546
  "rollup-plugin-visualizer": "^5.14.0",
3532
3547
  "vite-plugin-compression": "^0.5.1"
3533
3548
  };
3534
3549
  var APP_QUALITY_SCRIPTS = {
3535
- lint: "eslint src --ext .ts,.tsx --max-warnings 0",
3536
- "lint:fix": "eslint src --ext .ts,.tsx --fix",
3537
- format: 'prettier --write "src/**/*.{ts,tsx,css,json}"',
3538
- "format:check": 'prettier --check "src/**/*.{ts,tsx,css,json}"',
3539
3550
  "type-check": "tsc --noEmit -p tsconfig.json",
3540
- test: "vitest",
3541
- "test:ci": "vitest run",
3542
- coverage: "vitest run --coverage",
3543
- cleanup: "npm run lint:fix && npm run cleanup:imports && npm run type-check",
3544
- "cleanup:imports": "npm run lint:fix",
3545
- "cleanup:deadcode": "node ../../scripts/deadcode-scan-placeholder.js",
3546
3551
  "build:prod": "tsc -b && vite build --mode prod",
3547
3552
  analyze: "cross-env VITE_ANALYZE=1 tsc -b && vite build --mode prod"
3548
3553
  };
@@ -7931,7 +7936,7 @@ export function clearQueryCache(client: QueryClient): void {
7931
7936
  client.clear();
7932
7937
  }
7933
7938
  `;
7934
- const providerBody = `import type { ReactNode } from "react";
7939
+ const providerBody = `import React, { type ReactNode } from "react";
7935
7940
  import { QueryClientProvider } from "@tanstack/react-query";
7936
7941
  import { createAppQueryClient } from "./index";
7937
7942
 
@@ -7946,6 +7951,23 @@ export { queryClient };
7946
7951
  return {
7947
7952
  ...pkg(scope, "query-client", "./src/index.ts", { "@tanstack/react-query": "^5.66.9" }, indexBody),
7948
7953
  [`packages/query-client/src/QueryProvider.tsx`]: providerBody,
7954
+ [`packages/query-client/tsconfig.json`]: `${JSON.stringify(
7955
+ {
7956
+ compilerOptions: {
7957
+ target: "ES2022",
7958
+ module: "ESNext",
7959
+ moduleResolution: "Bundler",
7960
+ jsx: "react-jsx",
7961
+ strict: true,
7962
+ skipLibCheck: true,
7963
+ noEmit: true
7964
+ },
7965
+ include: ["src"]
7966
+ },
7967
+ null,
7968
+ 2
7969
+ )}
7970
+ `,
7949
7971
  [`packages/query-client/package.json`]: `${JSON.stringify(
7950
7972
  {
7951
7973
  name: `@${scope}/query-client`,
@@ -14250,16 +14272,35 @@ export function invalidateCache(cwd) {
14250
14272
  if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
14251
14273
  }
14252
14274
  `;
14253
- var ORCHESTRATOR = `import { spawn } from "node:child_process";
14275
+ var ORCHESTRATOR = `import fs from "node:fs";
14276
+ import path from "node:path";
14277
+ import { spawn } from "node:child_process";
14254
14278
  import { readCache, writeCache, hashInputs } from "./cache-engine.js";
14255
14279
  import { loadMfConfig } from "../tools/project-graph/graph-engine.js";
14256
14280
 
14281
+ function hasWorkspaceScript(projectRoot, script) {
14282
+ const pkgPath = path.join(projectRoot, "package.json");
14283
+ if (!fs.existsSync(pkgPath)) return false;
14284
+ try {
14285
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
14286
+ return typeof pkg.scripts?.[script] === "string" && pkg.scripts[script].trim().length > 0;
14287
+ } catch {
14288
+ return false;
14289
+ }
14290
+ }
14291
+
14257
14292
  export async function runTarget(projectId, target, options = {}) {
14258
14293
  const cwd = options.cwd ?? process.cwd();
14259
14294
  const config = loadMfConfig(cwd);
14260
14295
  const project = config.projects?.[projectId];
14261
14296
  if (!project) throw new Error("Unknown project: " + projectId);
14262
14297
 
14298
+ const projectRoot = path.join(cwd, project.root);
14299
+ if (!hasWorkspaceScript(projectRoot, target)) {
14300
+ console.log("[skip] " + projectId + ":" + target + " (no script in " + project.root + ")");
14301
+ return { success: true, code: 0, projectId, target, skipped: true };
14302
+ }
14303
+
14263
14304
  const cacheKey = hashInputs({ projectId, target, root: project.root });
14264
14305
  if (options.cache !== false && config.cache?.enabled !== false) {
14265
14306
  const cached = readCache(projectId + ":" + target + ":" + cacheKey, cwd);
@@ -14838,24 +14879,25 @@ export * from "./branding.js";
14838
14879
 
14839
14880
  // src/generators/platformFramework/index.ts
14840
14881
  init_developerExperience();
14841
- function getPlatformFrameworkFiles(projectName, allApps, sharedApp, npmScope) {
14882
+ function getPlatformFrameworkFiles(projectName, allApps, sharedApp, npmScope, options) {
14883
+ const lite = options?.lite ?? false;
14842
14884
  return {
14843
14885
  "mf.json": getMfJson(projectName, allApps, sharedApp),
14844
14886
  ...getMfCliFiles(),
14845
14887
  ...getProjectGraphFiles(),
14846
14888
  ...getTaskRunnerFiles(),
14847
14889
  ...getPlatformPluginFiles(),
14848
- ...getDevtoolsFiles(),
14849
- ...getCicdAndGovernanceFiles(projectName),
14850
- ...getTemplateMarketplaceFiles(),
14851
- ...getAiPlatformFiles(),
14852
- ...getExtendedSharedPackages(npmScope),
14890
+ ...lite ? {} : getDevtoolsFiles(),
14891
+ ...lite ? {} : getCicdAndGovernanceFiles(projectName),
14892
+ ...lite ? {} : getTemplateMarketplaceFiles(),
14893
+ ...lite ? {} : getAiPlatformFiles(),
14894
+ ...lite ? {} : getExtendedSharedPackages(npmScope),
14853
14895
  ...getRuntimeConfigFiles(),
14854
14896
  ...getArchitectureProtectionFiles(projectName, sharedApp, npmScope),
14855
14897
  ...getDeveloperExperiencePlatformFiles(projectName, allApps),
14856
14898
  "tools/mf-cli/schemas/mf.schema.json": getMfSchema(),
14857
14899
  "docs/PLATFORM_ARCHITECTURE.md": getPlatformArchitectureDoc(projectName),
14858
- ".vscode/mf.code-snippets": VSCODE_SNIPPETS
14900
+ ...lite ? {} : { ".vscode/mf.code-snippets": VSCODE_SNIPPETS }
14859
14901
  };
14860
14902
  }
14861
14903
  var VSCODE_SNIPPETS = `{
@@ -15154,6 +15196,7 @@ function buildConfigFromArgs(argv) {
15154
15196
  }
15155
15197
  const federationOs = toBoolean(options["federation-os"] ?? options.federationOs, false);
15156
15198
  const controlPlane = toBoolean(options["control-plane"] ?? options.controlPlane, false);
15199
+ const lite = toBoolean(options.lite, false);
15157
15200
  let config = {
15158
15201
  projectName: resolvedProjectName,
15159
15202
  port,
@@ -15165,7 +15208,8 @@ function buildConfigFromArgs(argv) {
15165
15208
  shared: resolveShared(options),
15166
15209
  features: resolveEnterpriseFeatures(options),
15167
15210
  federationOs,
15168
- controlPlane
15211
+ controlPlane,
15212
+ lite
15169
15213
  };
15170
15214
  if (controlPlane) {
15171
15215
  config = applyControlPlanePreset(config);
@@ -19787,6 +19831,13 @@ export function buildFederation() {
19787
19831
  // src/generators/project.ts
19788
19832
  var FEDERATION_SHARED_IMPORT = "../../../../packages/shared-config/src/federationShared.js";
19789
19833
  var FEDERATION_REMOTES_IMPORT = "../../../../packages/shared-config/src/federationRemotes.js";
19834
+ function getNpmrcContent() {
19835
+ return `install-strategy=hoisted
19836
+ fund=false
19837
+ audit=false
19838
+ prefer-offline=true
19839
+ `;
19840
+ }
19790
19841
  function getDockerfile(port) {
19791
19842
  return `FROM node:22-alpine
19792
19843
 
@@ -19896,13 +19947,11 @@ async function generateParentWorkspace(projectRoot, config) {
19896
19947
  Object.entries(getWorkspacePackageFiles(npmScope)).map(([filePath, content]) => writeFile(projectRoot, filePath, content))
19897
19948
  );
19898
19949
  if (isEnterpriseParent) {
19899
- const sdkIds = ["sdk-core", "api-client", "query-client", "websocket-sdk", ...enterpriseFlags.auth ? ["auth-sdk"] : []];
19950
+ const sdkIds = config.lite ? ["query-client"] : ["sdk-core", "api-client", "query-client", "websocket-sdk", ...enterpriseFlags.auth ? ["auth-sdk"] : []];
19900
19951
  await Promise.all(sdkIds.map((pkg2) => import_fs_extra3.default.ensureDir(import_node_path4.default.join(projectRoot, `packages/${pkg2}/src`))));
19901
- await Promise.all(
19902
- Object.entries(getSdkWorkspacePackageFiles(npmScope, enterpriseFlags)).map(
19903
- ([filePath, content]) => writeFile(projectRoot, filePath, content)
19904
- )
19905
- );
19952
+ const sdkFiles = getSdkWorkspacePackageFiles(npmScope, enterpriseFlags);
19953
+ const sdkEntries = config.lite ? Object.entries(sdkFiles).filter(([filePath]) => filePath.startsWith("packages/query-client/")) : Object.entries(sdkFiles);
19954
+ await Promise.all(sdkEntries.map(([filePath, content]) => writeFile(projectRoot, filePath, content)));
19906
19955
  }
19907
19956
  }
19908
19957
  await Promise.all(
@@ -19955,13 +20004,15 @@ async function generateParentWorkspace(projectRoot, config) {
19955
20004
  sharedApps[0] ?? "corelib",
19956
20005
  config.template === "tsx",
19957
20006
  includeWorkspacePackages,
19958
- includeWorkspacePackages && isEnterpriseParent ? enterpriseFlags : void 0,
20007
+ includeWorkspacePackages && isEnterpriseParent && !config.lite,
20008
+ includeWorkspacePackages && isEnterpriseParent && !config.lite ? enterpriseFlags : void 0,
19959
20009
  federationOs,
19960
20010
  controlPlane,
19961
- config.appType === "parent" && config.federation
20011
+ config.appType === "parent" && config.federation,
20012
+ config.lite
19962
20013
  )
19963
20014
  );
19964
- await writeFile(projectRoot, ".npmrc", "install-strategy=hoisted\nfund=false\n");
20015
+ await writeFile(projectRoot, ".npmrc", getNpmrcContent());
19965
20016
  await writeFile(
19966
20017
  projectRoot,
19967
20018
  "README.md",
@@ -19994,20 +20045,27 @@ async function generateParentWorkspace(projectRoot, config) {
19994
20045
  await writeFile(projectRoot, "scripts/security-scan-placeholder.js", getSecurityScanPlaceholderScript());
19995
20046
  if (config.template === "tsx" || config.template === "jsx" && isEnterpriseParent) {
19996
20047
  if (config.template === "tsx") {
19997
- await writeEnterpriseQualityRootFiles(projectRoot, config.projectName, allApps, true);
19998
- await augmentWorkspacePackagesTsconfig(projectRoot);
19999
- await Promise.all(allApps.map((appName) => augmentWorkspaceAppWithQuality(import_node_path4.default.join(projectRoot, "apps", appName))));
20048
+ if (config.lite) {
20049
+ await writeFile(projectRoot, "tsconfig.base.json", getTsconfigBaseJson());
20050
+ await augmentWorkspacePackagesTsconfig(projectRoot);
20051
+ } else {
20052
+ await writeEnterpriseQualityRootFiles(projectRoot, config.projectName, allApps, true);
20053
+ await augmentWorkspacePackagesTsconfig(projectRoot);
20054
+ await Promise.all(allApps.map((appName) => augmentWorkspaceAppWithQuality(import_node_path4.default.join(projectRoot, "apps", appName))));
20055
+ }
20000
20056
  }
20001
20057
  if (isEnterpriseParent) {
20002
20058
  await augmentWorkspacePackagesForProjectReferences(projectRoot);
20003
- await Promise.all(
20004
- Object.entries(getEnterprisePlatformRootFiles(config.projectName, enterpriseFlags)).map(
20005
- ([filePath, content]) => writeFile(projectRoot, filePath, content)
20006
- )
20007
- );
20059
+ if (!config.lite) {
20060
+ await Promise.all(
20061
+ Object.entries(getEnterprisePlatformRootFiles(config.projectName, enterpriseFlags)).map(
20062
+ ([filePath, content]) => writeFile(projectRoot, filePath, content)
20063
+ )
20064
+ );
20065
+ }
20008
20066
  const sharedAppName = sharedApps[0] ?? "corelib";
20009
20067
  await Promise.all(
20010
- Object.entries(getPlatformFrameworkFiles(config.projectName, allApps, sharedAppName, npmScope)).map(
20068
+ Object.entries(getPlatformFrameworkFiles(config.projectName, allApps, sharedAppName, npmScope, { lite: config.lite })).map(
20011
20069
  ([filePath, content]) => writeFile(projectRoot, filePath, content)
20012
20070
  )
20013
20071
  );
@@ -21628,7 +21686,7 @@ ${getFederationSharedBlockJs(enterprise, "host", platformPreset)}
21628
21686
  }
21629
21687
  `;
21630
21688
  }
21631
- function getWorkspacePackageJson(projectName, allApps, remotes, sharedApp, includeWorkspacePackages, includeEnterpriseQuality, enterpriseFlags, federationOs = false, controlPlane = false, includeInfraScripts = false) {
21689
+ function getWorkspacePackageJson(projectName, allApps, remotes, sharedApp, includeWorkspacePackages, includeEnterpriseQuality, enterpriseFlags, federationOs = false, controlPlane = false, includeInfraScripts = false, lite = false) {
21632
21690
  const envs = ["dev", "staging", "uat", "production"];
21633
21691
  const hostApp = allApps[0];
21634
21692
  const scripts = {
@@ -21700,7 +21758,7 @@ function getWorkspacePackageJson(projectName, allApps, remotes, sharedApp, inclu
21700
21758
  remotes
21701
21759
  }
21702
21760
  };
21703
- if (includeEnterpriseQuality) {
21761
+ if (includeEnterpriseQuality && !lite) {
21704
21762
  applyEnterpriseQualityRootPackageJson(packageJson.scripts, packageJson.devDependencies, allApps);
21705
21763
  packageJson.overrides = getWorkspaceNpmOverrides();
21706
21764
  }
@@ -21897,7 +21955,7 @@ async function resolveWorkspaceContext(workspaceRoot) {
21897
21955
  const controlPlane = !federationOs && await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, "apps/workflow-monitor-app"));
21898
21956
  const includeWorkspacePackages = await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, "packages/shared-ui"));
21899
21957
  const includeInfraScripts = await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, "infrastructure/nginx"));
21900
- const includeEnterpriseQuality = template === "tsx" && (enterprise || includeWorkspacePackages);
21958
+ const includeEnterpriseQuality = template === "tsx" && (enterprise || includeWorkspacePackages) && await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, ".eslintrc.cjs"));
21901
21959
  return {
21902
21960
  workspaceRoot,
21903
21961
  projectName: mf.name ?? import_node_path4.default.basename(workspaceRoot),
@@ -22094,6 +22152,7 @@ function printHelp() {
22094
22152
  console.log(" --shared=sharedlib");
22095
22153
  console.log(" --docker=true|false (default: true)");
22096
22154
  console.log(" --federation=true|false");
22155
+ console.log(" --lite Faster npm install (skip ESLint/Vitest/Husky + optional SDK packages)");
22097
22156
  console.log("");
22098
22157
  console.log("Enterprise platform flags (parent monorepo):");
22099
22158
  console.log(" --auth OAuth/OIDC auth foundation");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multisystemsuite/create-mf-app",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Production-ready TypeScript CLI to scaffold enterprise microfrontend apps with React, Vite and Module Federation.",
5
5
  "license": "MIT",
6
6
  "keywords": [