CLI
Nine commands that scaffold modules, wire a language end to end, and keep the project tidy. It ships with the template and needs no global install.
Running it
npm run smart-admin -- <command> [args]The -- is npm's "pass the rest to the script" separator. Without it npm eats the flags itself.
Two commands have their own shortcut in package.json:
npm run generate post
npm run analyzeThe commands
| Command | What it does |
|---|---|
generate <name> | Scaffolds a list module from its block in smart-admin.generate.json. |
generate-form <name> | Generates a form page from its block in smart-admin.form.json. |
add-locale <code> | Wires a new language end to end. |
remove-locale <code> | Strips a language completely, using its manifest. |
analyze | Reports components, fonts and translation coverage. |
remove <target> | Removes a file, refusing if anything still imports it. |
restore <target> | Puts one removed file back. |
reset | Puts every removed file back. |
remove-backups | Deletes the backups themselves. |
smart-admin --help prints that list. generate, generate-form, add-locale, remove-locale and remove-backups each take --help too; analyze, remove, restore and reset do not, and their usage is on the pages here.
Declare, do not guess
The two generators read a JSON file you write. Neither of them infers anything from a name.
generate reads the JSON type of each value in your sample row — a string is text, a number is a number, an object is nested. It never reads meaning out of a key: a field called cover is not an image and created_at is not a date until you say so in annotations. That is the whole point of the input file.
generate-form reads no API response at all, because a form is not derivable from one. Whether a field is required, how short it may be, which options it allows, which step it belongs to — none of that is ever in a response.
Both refuse rather than improvise. Ask for a block that is not there and the command names what it did find, prints the shortest block that would work, and writes nothing:
smart-admin.generate.json has no "widget" block.
It declares: post
Add one, then run this again. The shortest block that works:
"widget": {
"rowsPath": "data",
"row": { "id": 1, "name": "Example" },
"filterFields": []
}Both also have --dry-run, which prints the plan and writes nothing.
Two files, one committed
Each generator has an example file that ships with the template and a real file that does not:
| Format reference, committed | Your shapes, gitignored |
|---|---|
smart-admin.generate.example.json | smart-admin.generate.json |
smart-admin.form.example.json | smart-admin.form.json |
On the first run the CLI copies the example to the real name and makes sure that path is in .gitignore, so a real API response never reaches your repository. It says so when it does:
Created smart-admin.generate.json from the example.--dry-run will not create it — there would be nothing to dry-run against — and it says that too.
Backups
Everything that deletes takes a backup first, into .smart-admin/backups/{timestamp}/, keeping the original paths. .gitignore already covers that directory, and the CLI says so rather than adding a second rule.
--backup and --no-backup answer that question up front, so a script or a CI run never blocks waiting for a terminal that is not there. To keep backups out of non-development runs entirely, create .smart-admin/config.json:
{
"backups": {
"devOnly": true
}
}Backups are then skipped whenever NODE_ENV=production or CI is set. An explicit --backup still wins over it.
Restore one file with restore, all of them with reset, and delete the backups with remove-backups.
What it never generates
Permissions, the API call and its auth, the pagination request contract, what belongs in a store, and — for a form — the submit and load requests. None of them can be known from a sample row or a field list, so the generated page marks the spot instead: a list page carries a fetch data will be here marker, and a form page carries one for the submit and one for the load.
The CLI prints that list at the end of every generate, so it reads as a handover rather than an omission.
After generating
Restart the dev server. unplugin-vue-router builds its route table at boot, so a new page does not appear until it restarts. Every generate says so.
Translations are injected into every file in src/locales/: real words in the base language, the same words as placeholders in the other eight. Run analyze --locales to see what is still untranslated.
Where to go next
generate— the list module.generate-form— the form page.- Add a module — the whole flow, end to end.
- Add a language — the same for a locale.