create-ec-app
v1.11.0
Published
Unified CLI tool to create different types of EC applications: Webresource, Portal, Power Pages, Power Apps Code Apps
Maintainers
Readme
create-ec-app
CLI for scaffolding Enterprisecloud React apps from layered templates.
Scaffold An App
npx create-ec-app@latest --project-name my-webresource --target webresource --ui shadcn-ui
npx create-ec-app@latest --project-name my-code-app --target code-apps --ui kendo --skip-git
npx create-ec-app@latest --project-name my-swa --target swa --ui shadcn-ui --no-installFor local development in this repo:
npm run dev -- --project-name my-app --target webresource --ui shadcn-ui --no-install --skip-gitTargets include webresource, portal, power-pages, swa, and code-apps. UI layers include shadcn-ui and kendo.
Quick shadcn creates with dependency install:
npx create-ec-app@latest --project-name my-webresource --target webresource --ui shadcn-ui --install
npx create-ec-app@latest --project-name my-power-pages --target power-pages --ui shadcn-ui --install
npx create-ec-app@latest --project-name my-swa --target swa --ui shadcn-ui --install
npx create-ec-app@latest --project-name my-code-app --target code-apps --ui shadcn-ui --installUseful CLI flags:
--force: overwrite an existing non-empty project directory.--skip-git: skipgit init,git add ., and the initial commit.--help/-h: print usage and exit without prompting.
By default, existing non-empty project directories fail with a clear error. Existing empty directories are reused. Git initialization still runs by default unless --skip-git is passed.
Power Apps Code Apps
Use the code-apps target to scaffold a React + Vite app that keeps the EC base template and adds Microsoft Power Apps code app support:
npm run dev -- --project-name my-code-app --target code-apps --ui shadcn-ui --no-install --skip-gitCompared with the base template, the Code Apps target adds:
@microsoft/power-appsfor the Power Apps code app SDK and npm CLI@microsoft/power-apps-vitefor local Play URLs, Power Apps CORS settings, andbase: "./"build outputpowerApps()invite.config.tsalongside the existing React, Tailwind, and@alias config
Code Apps use the Power Apps host, power.config.json, and generated SDK services for Dataverse and connector access. They should not use the webresource target's src/services/AuthService.ts or token.json local bearer-token pattern, and the Code Apps scaffold removes those auth artifacts if they are present during generation.
After scaffolding, initialize the app metadata and authenticate to your environment:
npm install
npx power-apps init --display-name "My Code App" --environment-id <environment-id> --app-url http://localhost:5173power.config.json is intentionally not scaffolded as a real file. Microsoft’s CLI creates it during npx power-apps init and refuses to initialize if one already exists. The target includes power.config.example.json so you can see the expected shape without blocking initialization.
For local development, run:
npm run devOpen the Local Play URL printed by the Power Apps Vite plugin in the same browser profile you use for your Power Platform tenant.
To deploy:
npm run build
npx power-apps pushnpx power-apps push publishes a new version to the environment in power.config.json and returns a Power Apps URL when it succeeds. Microsoft still documents the older PAC CLI path (pac code init, npm run build | pac code push) for compatibility, but the npm CLI is the preferred path for new code app work.
For ALM, use a non-default solution or set a preferred solution in the environment. With the PAC CLI path you can target a specific solution using:
pac code push --solutionName <solution-name>The generated Code Apps README links to Microsoft's overview, npm CLI quickstart, architecture, and ALM documentation.
It also documents Microsoft’s Code Apps data model:
- Dataverse table data sources through generated services under
src/generated - Power Platform connector data sources with connection IDs or connection references
- Environment-variable references for ALM-friendly dataset/table values
- Dataverse actions/functions through
npx power-apps find-dataverse-apiandnpx power-apps add-dataverse-api - Runtime context through
getContextfrom@microsoft/power-apps/app
shadcn/ui Template
The shadcn template vendors generated component source under templates/ui/shadcn-ui/src. Normal scaffolding copies those files like any other template layer and does not run npx shadcn.
npm run dev -- --project-name my-app --target webresource --ui shadcn-ui --no-install --skip-gitThat command is offline-friendly after the package is available locally; --no-install skips dependency installation and no scaffold-time external component generation is performed. Generated projects still include components.json so app owners can later run shadcn manually inside their own app if they choose.
The committed snapshot pins the component source and uses exact versions in templates/ui/shadcn-ui/package.patch.json for dependencies directly required by those files.
To scaffold shadcn files from a custom registry instead of the committed snapshot, pass a built registry.json URL:
npx create-ec-app@latest --project-name my-app --target webresource --ui shadcn-ui --shadcn-registry https://schalk-conradie.github.io/ec-registry/r/registry.json --no-install --skip-gitCustom registry scaffolding fetches every item listed in that registry catalog, writes each item file using its target or path, and merges the item dependencies and devDependencies into the generated package.json. It still writes components.json, the shadcn Tailwind CSS import, and a registry namespace so the generated app can use the same registry later.
Maintainers refresh the snapshot intentionally:
npm run refresh:shadcn-templateThe refresh script uses a pinned shadcn CLI version, applies EC portal compatibility transforms, updates vendored component files, writes SHADCN_TEMPLATE.md, and refreshes the shadcn dependency patch.
For shadcn changes, generate a fresh app and run a build before committing:
npm run build:generatedStyle Scoping
Generated webresources use normal Tailwind/shadcn CSS by default. When a webresource is converted into a PCF wrapper, the generator creates a local PCF shell and a local pcf-scoped.css file in the generated PCF project. That CSS file is copied from the built dist/main.css with every non-keyframe selector rewritten under the .pcf-shell-control[data-pcf-control="..."] host selector, so Tailwind utilities, base selectors, and shadcn/Tailwind theme variables stay inside that control.
Template Updates
To refresh template dependency ranges and lockfiles:
bash update-templates.shThe script updates package.patch.json files, template package.json files, installs dependencies to refresh lockfiles, then removes template node_modules directories.
Generate a PCF Control
If you want to host the React webresource inside a PCF control instead of loading the HTML webresource directly in an iframe, use create-ec-app itself to generate the wrapper for an existing webresource project.
Basic flow:
- Build the webresource:
npm run build- Run the generator from the webresource root. Point
--pcf-dirat the webresource root and--outputat the PCF project folder you want to generate:
npx create-ec-app@latest \
--pcf-dir . \
--output ./pcf/{{ControlName}} \
--namespace EC \
--constructor {{ControlName}} \
--display-name "Control Name"- Install dependencies inside that generated PCF directory:
cd ./pcf/{{ControlName}}
npm install
npm run buildThis writes a standalone PCF project to the --pcf-dir folder. The generated control:
- imports
src/App.tsxdirectly instead of wrapping built HTML in an iframe - creates and imports
pcf-scoped.cssfrom the builtdist/main.css - scopes every non-keyframe CSS selector under the generated PCF host selector
- creates
src/runtime/types.tsonly if that file does not already exist - provides a runtime object with record context and
context.webAPIaccess inside the generated PCF shell, following thePcfBasepattern - mounts your React app directly into the PCF container
Regenerate after app code or CSS changes by running the same sequence again from the webresource root:
npm run build
npx create-ec-app@latest \
--pcf-dir . \
--output ./pcf/FusionNotebookHost \
--namespace EC \
--constructor FusionNotebookHost \
--display-name "Fusion Notebook Host"
cd pcf/FusionNotebookHost
npm install
npm run buildRegeneration removes and recreates the PCF output folder, so keep durable app code in src and use generator templates or layers for repeatable PCF-specific changes.
What gets generated:
- a minimal PCF wrapper project under
pcf/<ConstructorName> - a checked-in PCF shell stamped out from
create-ec-app/templates/pcf/base - direct imports back to your webresource source
- a generated
pcf-scoped.cssfile with CSS selectors scoped to the PCF control
What does not happen:
- your existing webresource project is not converted in place
- your React source is not moved into the PCF project
- the generated PCF project does not automatically get added to a Dataverse solution
Verification
Useful checks before shipping template changes:
npm run build
npm test
npm run smoke:scaffold
npm run build:generated
node scripts/check-generated-css-scope.mjs <generated-pcf-control-path>npm test runs Vitest with coverage across all src/**/*.ts files and enforces 100% statement, branch, function, and line coverage. npm run smoke:scaffold builds the CLI, scaffolds the target/UI matrix with --no-install --skip-git, and checks the generated file shape. npm run build:generated installs, builds, and lints a smaller generated-project matrix, including a shadcn webresource.
Release Process
Releases use semantic-release from .github/workflows/ci.yml on pushes to main.
The CI job runs:
npm ci
npm run build
npm test
npm run smoke:scaffoldAfter CI passes, the release job runs:
npm ci
npm run releaseRelease authentication:
GITHUB_TOKEN: provided by GitHub Actions.- npm trusted publishing is configured for this repository's
ci.ymlworkflow on npmjs.com.
Use Conventional Commits so semantic-release can choose the release type. Git tags, GitHub releases, and the npm package version published by semantic-release are the source of truth; package.json is not manually bumped as part of normal development.
