Getting started
pageslens reads a Power Pages site from the files in git: a site downloaded with the Power Platform CLI, or an unpacked solution that holds the site. It needs no Dataverse connection, and the same folder gives the same output every run.
Requires Node.js 22.23.4+, 24.22.1+ or 26.12.1+.
Install
Section titled “Install”Run it without installing:
npx pageslens lint path/to/siteOr add it to the project, so everyone and CI run the same version:
npm install --save-dev pageslensThe commands
Section titled “The commands”npx pageslens lint path/to/sitenpx pageslens test path/to/sitenpx pageslens diff origin/main HEAD --site path/to/sitenpx pageslens align path/to/site path/to/solutionnpx pageslens docs path/to/site --out docs/sitelintfinds known mistakes in table permissions, web roles, Web API settings and site settings.testchecks the site against your own requirements, written as sentences in a policy file.diffshows what a change does to who can see and change what.alignchecks that a download and the solution that deploys it describe the same site.docswrites an access matrix, the Web API surface and a diagram as Markdown.explain <rule>prints a lint rule’s page: why it matters and how to fix it.explainalone lists the rules.
The site folder
Section titled “The site folder”path/to/site is any of these:
- the
.powerpages-sitefolder thatpac pages download-code-sitewrites, or the folder that contains it; - a classic site folder that
pac pages downloadwrites (the one withwebsite.ymlandwebrole.yml); - an unpacked solution (below).
pageslens tells the two download layouts apart by the keys in website.yml.
Records that are inactive in Dataverse (statecode: 1 in the download) are left out everywhere, as Power Pages ignores them: an inactive table permission grants nothing, and an inactive role or setting counts for nothing. docs says how many were left out.
pageslens has been verified on enhanced data model sites, both a code site (pac pages download-code-site) and a classic site (pac pages download), and on unpacked solutions of both. pac pages download writes the same layout with --modelVersion 1 and 2, and a standard data model site should download in it too, but neither has been checked against a real site yet.
Solutions
Section titled “Solutions”An unpacked solution is the folder with Other/Solution.xml that pac solution unpack writes, or the project pac solution clone writes (pageslens looks in its src/). Each site component is powerpagecomponents/<guid>/powerpagecomponent.xml, and pageslens reads them into the same records as a download of the same site, matched by GUID, so lint, test, diff and docs give the same results. Findings point at the component’s file.
- A solution with more than one site isn’t read yet, nor is the YAML source control format that Dataverse Git integration writes, nor a solution
.zip(unpack it first). - A component that isn’t in the solution isn’t checked: pageslens reads what deploys.
- To check that a download and a solution describe the same site, use
align. - A site setting can take its value from an environment variable. pageslens reads the variable’s value from the solution (the current value, or else the default). A download doesn’t include the variable, so there the value is unknown: rules that need it skip the setting,
teststatements that need it fail and say why, anddiffcounts a move to a variable as wider. To check the values a stage gets, give its deployment settings file.
Deployment settings
Section titled “Deployment settings”The values of environment variables differ per stage, and the import takes each stage’s values from a deployment settings file: the JSON pac solution create-settings writes and pac solution import --settings-file reads, usually kept in the repository as deployment-settings/<stage>.json. Give one to lint, test, diff or align with --settings <file> to check what that stage gets:
pageslens lint site --settings deployment-settings/prod.jsonpageslens test site --settings deployment-settings/prod.json- A setting backed by a variable takes the file’s
Valuefor it, matched by schema name, ignoring case, and messages name the file:"true" from environment variable dwcrm_ContactApi in deployment-settings/prod.json. This works on a download and on a solution alike. - A variable the file doesn’t have, or has with an empty
Value(ascreate-settingswrites every variable), keeps what it had: unknown in a download, the solution’s value in a solution. So does a secret (TypeId100000005): its value is in Azure Key Vault. pageslens doesn’t guess what the import does then. - Only
EnvironmentVariablesis read;ConnectionReferencesand other keys are ignored, and so are variables no setting uses andDefaultValue. - One stage per run: check several stages with several runs.
docsdoesn’t take the file, so committed docs stay the same for every stage. - In
diff, the file is read on each side: from the folder, or at the same path in each commit, so a pull request that only changes a stage’s file shows what it does to access. A side without the file reads it as empty. - In
align, a variable the file gives a value isn’t reported as having none, and a variable the file gives no value is a warning (variable-not-in-settings) when the solution has a value: the stage may get dev’s.
A file that can’t be read, isn’t JSON, or has no EnvironmentVariables list of entries with a SchemaName is exit code 2.