@multisystemsuite/create-mf-app 1.0.14 → 1.0.15

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 +88 -46
  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
  };
@@ -14250,16 +14255,35 @@ export function invalidateCache(cwd) {
14250
14255
  if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
14251
14256
  }
14252
14257
  `;
14253
- var ORCHESTRATOR = `import { spawn } from "node:child_process";
14258
+ var ORCHESTRATOR = `import fs from "node:fs";
14259
+ import path from "node:path";
14260
+ import { spawn } from "node:child_process";
14254
14261
  import { readCache, writeCache, hashInputs } from "./cache-engine.js";
14255
14262
  import { loadMfConfig } from "../tools/project-graph/graph-engine.js";
14256
14263
 
14264
+ function hasWorkspaceScript(projectRoot, script) {
14265
+ const pkgPath = path.join(projectRoot, "package.json");
14266
+ if (!fs.existsSync(pkgPath)) return false;
14267
+ try {
14268
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
14269
+ return typeof pkg.scripts?.[script] === "string" && pkg.scripts[script].trim().length > 0;
14270
+ } catch {
14271
+ return false;
14272
+ }
14273
+ }
14274
+
14257
14275
  export async function runTarget(projectId, target, options = {}) {
14258
14276
  const cwd = options.cwd ?? process.cwd();
14259
14277
  const config = loadMfConfig(cwd);
14260
14278
  const project = config.projects?.[projectId];
14261
14279
  if (!project) throw new Error("Unknown project: " + projectId);
14262
14280
 
14281
+ const projectRoot = path.join(cwd, project.root);
14282
+ if (!hasWorkspaceScript(projectRoot, target)) {
14283
+ console.log("[skip] " + projectId + ":" + target + " (no script in " + project.root + ")");
14284
+ return { success: true, code: 0, projectId, target, skipped: true };
14285
+ }
14286
+
14263
14287
  const cacheKey = hashInputs({ projectId, target, root: project.root });
14264
14288
  if (options.cache !== false && config.cache?.enabled !== false) {
14265
14289
  const cached = readCache(projectId + ":" + target + ":" + cacheKey, cwd);
@@ -14838,24 +14862,25 @@ export * from "./branding.js";
14838
14862
 
14839
14863
  // src/generators/platformFramework/index.ts
14840
14864
  init_developerExperience();
14841
- function getPlatformFrameworkFiles(projectName, allApps, sharedApp, npmScope) {
14865
+ function getPlatformFrameworkFiles(projectName, allApps, sharedApp, npmScope, options) {
14866
+ const lite = options?.lite ?? false;
14842
14867
  return {
14843
14868
  "mf.json": getMfJson(projectName, allApps, sharedApp),
14844
14869
  ...getMfCliFiles(),
14845
14870
  ...getProjectGraphFiles(),
14846
14871
  ...getTaskRunnerFiles(),
14847
14872
  ...getPlatformPluginFiles(),
14848
- ...getDevtoolsFiles(),
14849
- ...getCicdAndGovernanceFiles(projectName),
14850
- ...getTemplateMarketplaceFiles(),
14851
- ...getAiPlatformFiles(),
14852
- ...getExtendedSharedPackages(npmScope),
14873
+ ...lite ? {} : getDevtoolsFiles(),
14874
+ ...lite ? {} : getCicdAndGovernanceFiles(projectName),
14875
+ ...lite ? {} : getTemplateMarketplaceFiles(),
14876
+ ...lite ? {} : getAiPlatformFiles(),
14877
+ ...lite ? {} : getExtendedSharedPackages(npmScope),
14853
14878
  ...getRuntimeConfigFiles(),
14854
14879
  ...getArchitectureProtectionFiles(projectName, sharedApp, npmScope),
14855
14880
  ...getDeveloperExperiencePlatformFiles(projectName, allApps),
14856
14881
  "tools/mf-cli/schemas/mf.schema.json": getMfSchema(),
14857
14882
  "docs/PLATFORM_ARCHITECTURE.md": getPlatformArchitectureDoc(projectName),
14858
- ".vscode/mf.code-snippets": VSCODE_SNIPPETS
14883
+ ...lite ? {} : { ".vscode/mf.code-snippets": VSCODE_SNIPPETS }
14859
14884
  };
14860
14885
  }
14861
14886
  var VSCODE_SNIPPETS = `{
@@ -15154,6 +15179,7 @@ function buildConfigFromArgs(argv) {
15154
15179
  }
15155
15180
  const federationOs = toBoolean(options["federation-os"] ?? options.federationOs, false);
15156
15181
  const controlPlane = toBoolean(options["control-plane"] ?? options.controlPlane, false);
15182
+ const lite = toBoolean(options.lite, false);
15157
15183
  let config = {
15158
15184
  projectName: resolvedProjectName,
15159
15185
  port,
@@ -15165,7 +15191,8 @@ function buildConfigFromArgs(argv) {
15165
15191
  shared: resolveShared(options),
15166
15192
  features: resolveEnterpriseFeatures(options),
15167
15193
  federationOs,
15168
- controlPlane
15194
+ controlPlane,
15195
+ lite
15169
15196
  };
15170
15197
  if (controlPlane) {
15171
15198
  config = applyControlPlanePreset(config);
@@ -19787,6 +19814,13 @@ export function buildFederation() {
19787
19814
  // src/generators/project.ts
19788
19815
  var FEDERATION_SHARED_IMPORT = "../../../../packages/shared-config/src/federationShared.js";
19789
19816
  var FEDERATION_REMOTES_IMPORT = "../../../../packages/shared-config/src/federationRemotes.js";
19817
+ function getNpmrcContent() {
19818
+ return `install-strategy=hoisted
19819
+ fund=false
19820
+ audit=false
19821
+ prefer-offline=true
19822
+ `;
19823
+ }
19790
19824
  function getDockerfile(port) {
19791
19825
  return `FROM node:22-alpine
19792
19826
 
@@ -19896,13 +19930,11 @@ async function generateParentWorkspace(projectRoot, config) {
19896
19930
  Object.entries(getWorkspacePackageFiles(npmScope)).map(([filePath, content]) => writeFile(projectRoot, filePath, content))
19897
19931
  );
19898
19932
  if (isEnterpriseParent) {
19899
- const sdkIds = ["sdk-core", "api-client", "query-client", "websocket-sdk", ...enterpriseFlags.auth ? ["auth-sdk"] : []];
19933
+ const sdkIds = config.lite ? ["query-client"] : ["sdk-core", "api-client", "query-client", "websocket-sdk", ...enterpriseFlags.auth ? ["auth-sdk"] : []];
19900
19934
  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
- );
19935
+ const sdkFiles = getSdkWorkspacePackageFiles(npmScope, enterpriseFlags);
19936
+ const sdkEntries = config.lite ? Object.entries(sdkFiles).filter(([filePath]) => filePath.startsWith("packages/query-client/")) : Object.entries(sdkFiles);
19937
+ await Promise.all(sdkEntries.map(([filePath, content]) => writeFile(projectRoot, filePath, content)));
19906
19938
  }
19907
19939
  }
19908
19940
  await Promise.all(
@@ -19955,13 +19987,15 @@ async function generateParentWorkspace(projectRoot, config) {
19955
19987
  sharedApps[0] ?? "corelib",
19956
19988
  config.template === "tsx",
19957
19989
  includeWorkspacePackages,
19958
- includeWorkspacePackages && isEnterpriseParent ? enterpriseFlags : void 0,
19990
+ includeWorkspacePackages && isEnterpriseParent && !config.lite,
19991
+ includeWorkspacePackages && isEnterpriseParent && !config.lite ? enterpriseFlags : void 0,
19959
19992
  federationOs,
19960
19993
  controlPlane,
19961
- config.appType === "parent" && config.federation
19994
+ config.appType === "parent" && config.federation,
19995
+ config.lite
19962
19996
  )
19963
19997
  );
19964
- await writeFile(projectRoot, ".npmrc", "install-strategy=hoisted\nfund=false\n");
19998
+ await writeFile(projectRoot, ".npmrc", getNpmrcContent());
19965
19999
  await writeFile(
19966
20000
  projectRoot,
19967
20001
  "README.md",
@@ -19994,20 +20028,27 @@ async function generateParentWorkspace(projectRoot, config) {
19994
20028
  await writeFile(projectRoot, "scripts/security-scan-placeholder.js", getSecurityScanPlaceholderScript());
19995
20029
  if (config.template === "tsx" || config.template === "jsx" && isEnterpriseParent) {
19996
20030
  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))));
20031
+ if (config.lite) {
20032
+ await writeFile(projectRoot, "tsconfig.base.json", getTsconfigBaseJson());
20033
+ await augmentWorkspacePackagesTsconfig(projectRoot);
20034
+ } else {
20035
+ await writeEnterpriseQualityRootFiles(projectRoot, config.projectName, allApps, true);
20036
+ await augmentWorkspacePackagesTsconfig(projectRoot);
20037
+ await Promise.all(allApps.map((appName) => augmentWorkspaceAppWithQuality(import_node_path4.default.join(projectRoot, "apps", appName))));
20038
+ }
20000
20039
  }
20001
20040
  if (isEnterpriseParent) {
20002
20041
  await augmentWorkspacePackagesForProjectReferences(projectRoot);
20003
- await Promise.all(
20004
- Object.entries(getEnterprisePlatformRootFiles(config.projectName, enterpriseFlags)).map(
20005
- ([filePath, content]) => writeFile(projectRoot, filePath, content)
20006
- )
20007
- );
20042
+ if (!config.lite) {
20043
+ await Promise.all(
20044
+ Object.entries(getEnterprisePlatformRootFiles(config.projectName, enterpriseFlags)).map(
20045
+ ([filePath, content]) => writeFile(projectRoot, filePath, content)
20046
+ )
20047
+ );
20048
+ }
20008
20049
  const sharedAppName = sharedApps[0] ?? "corelib";
20009
20050
  await Promise.all(
20010
- Object.entries(getPlatformFrameworkFiles(config.projectName, allApps, sharedAppName, npmScope)).map(
20051
+ Object.entries(getPlatformFrameworkFiles(config.projectName, allApps, sharedAppName, npmScope, { lite: config.lite })).map(
20011
20052
  ([filePath, content]) => writeFile(projectRoot, filePath, content)
20012
20053
  )
20013
20054
  );
@@ -21628,7 +21669,7 @@ ${getFederationSharedBlockJs(enterprise, "host", platformPreset)}
21628
21669
  }
21629
21670
  `;
21630
21671
  }
21631
- function getWorkspacePackageJson(projectName, allApps, remotes, sharedApp, includeWorkspacePackages, includeEnterpriseQuality, enterpriseFlags, federationOs = false, controlPlane = false, includeInfraScripts = false) {
21672
+ function getWorkspacePackageJson(projectName, allApps, remotes, sharedApp, includeWorkspacePackages, includeEnterpriseQuality, enterpriseFlags, federationOs = false, controlPlane = false, includeInfraScripts = false, lite = false) {
21632
21673
  const envs = ["dev", "staging", "uat", "production"];
21633
21674
  const hostApp = allApps[0];
21634
21675
  const scripts = {
@@ -21700,7 +21741,7 @@ function getWorkspacePackageJson(projectName, allApps, remotes, sharedApp, inclu
21700
21741
  remotes
21701
21742
  }
21702
21743
  };
21703
- if (includeEnterpriseQuality) {
21744
+ if (includeEnterpriseQuality && !lite) {
21704
21745
  applyEnterpriseQualityRootPackageJson(packageJson.scripts, packageJson.devDependencies, allApps);
21705
21746
  packageJson.overrides = getWorkspaceNpmOverrides();
21706
21747
  }
@@ -21897,7 +21938,7 @@ async function resolveWorkspaceContext(workspaceRoot) {
21897
21938
  const controlPlane = !federationOs && await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, "apps/workflow-monitor-app"));
21898
21939
  const includeWorkspacePackages = await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, "packages/shared-ui"));
21899
21940
  const includeInfraScripts = await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, "infrastructure/nginx"));
21900
- const includeEnterpriseQuality = template === "tsx" && (enterprise || includeWorkspacePackages);
21941
+ const includeEnterpriseQuality = template === "tsx" && (enterprise || includeWorkspacePackages) && await import_fs_extra3.default.pathExists(import_node_path4.default.join(workspaceRoot, ".eslintrc.cjs"));
21901
21942
  return {
21902
21943
  workspaceRoot,
21903
21944
  projectName: mf.name ?? import_node_path4.default.basename(workspaceRoot),
@@ -22094,6 +22135,7 @@ function printHelp() {
22094
22135
  console.log(" --shared=sharedlib");
22095
22136
  console.log(" --docker=true|false (default: true)");
22096
22137
  console.log(" --federation=true|false");
22138
+ console.log(" --lite Faster npm install (skip ESLint/Vitest/Husky + optional SDK packages)");
22097
22139
  console.log("");
22098
22140
  console.log("Enterprise platform flags (parent monorepo):");
22099
22141
  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.15",
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": [