create-procode 0.0.0-stage → 1.1.0
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.
- package/README.md +266 -2
- package/bin/create-procode.mjs +9 -0
- package/package.json +30 -4
- package/src/args.mjs +145 -0
- package/src/cli.mjs +514 -0
- package/src/deps.mjs +208 -0
- package/src/log.mjs +23 -0
- package/src/multiLanguage.mjs +409 -0
- package/src/prompt.mjs +60 -0
- package/src/scaffold.mjs +104 -0
- package/templates/base/README.md.tmpl +185 -0
- package/templates/base/_dotenv.local.example.tmpl +8 -0
- package/templates/base/_dotenv.tmpl +41 -0
- package/templates/base/_doteslintrc.cjs.tmpl +125 -0
- package/templates/base/_dotgitignore.tmpl +32 -0
- package/templates/base/_dotnvmrc.tmpl +1 -0
- package/templates/base/_dotvscode/extensions.json +3 -0
- package/templates/base/index.html.tmpl +18 -0
- package/templates/base/package.json.tmpl +15 -0
- package/templates/base/public/favicon.svg +4 -0
- package/templates/base/src/AdapterProvider/AdapterFactory.ts.tmpl +21 -0
- package/templates/base/src/AdapterProvider/SampleContacts.ts.tmpl +136 -0
- package/templates/base/src/CustomAction.ts.tmpl +47 -0
- package/templates/base/src/CustomAuthorizationProvider.ts.tmpl +77 -0
- package/templates/base/src/Factory/Actions/myAction.ts.tmpl +51 -0
- package/templates/base/src/Factory/Actions/sampleContacts.ts.tmpl +244 -0
- package/templates/base/src/Factory/Actions/setField.ts.tmpl +20 -0
- package/templates/base/src/Factory/CustomRuleEffectFactory.ts.tmpl +30 -0
- package/templates/base/src/Factory/CustomValidationFactory.tsx.tmpl +24 -0
- package/templates/base/src/Factory/CustomWidgetFactory.tsx.tmpl +43 -0
- package/templates/base/src/Factory/RuleEffects/readOnlyEffect.ts.tmpl +27 -0
- package/templates/base/src/Factory/Validations/myValidation.ts.tmpl +52 -0
- package/templates/base/src/Factory/Widgets/MyWidget/index.tsx.tmpl +101 -0
- package/templates/base/src/Factory/Widgets/Notification/index.tsx.tmpl +53 -0
- package/templates/base/src/Factory/Widgets/Notification/notifiFunc.ts.tmpl +9 -0
- package/templates/base/src/Factory/Widgets/SectionNav/index.tsx +193 -0
- package/templates/base/src/Factory/Widgets/SectionNav/style.scss +139 -0
- package/templates/base/src/Module/Sample/Contacts.json.tmpl +965 -0
- package/templates/base/src/Module/index.ts.tmpl +26 -0
- package/templates/base/src/Screens/index.tsx.tmpl +45 -0
- package/templates/base/src/Utils/errorMessages.ts.tmpl +35 -0
- package/templates/base/src/Utils/fieldErrorMapper.ts.tmpl +46 -0
- package/templates/base/src/Utils/logger.ts.tmpl +21 -0
- package/templates/base/src/Utils/responseFormatter.ts.tmpl +39 -0
- package/templates/base/src/Utils/storages.ts.tmpl +35 -0
- package/templates/base/src/assets/styles/index.scss.tmpl +149 -0
- package/templates/base/src/constant.ts.tmpl +53 -0
- package/templates/base/src/icons.ts.tmpl +17 -0
- package/templates/base/src/localizationResolver.ts.tmpl +48 -0
- package/templates/base/src/main.tsx.tmpl +136 -0
- package/templates/base/src/routes.ts.tmpl +20 -0
- package/templates/base/src/vite-env.d.ts.tmpl +20 -0
- package/templates/base/tsconfig.json.tmpl +30 -0
- package/templates/base/tsconfig.node.json.tmpl +11 -0
- package/templates/base/vite.config.ts.tmpl +90 -0
- package/templates/multi-language/locales/fr/core.json +76 -0
- package/templates/multi-language/locales/fr/kendo.json +181 -0
- package/templates/multi-language/locales/fr/template.json +169 -0
- package/templates/multi-language/locales/fr/widget.json +65 -0
- package/templates/multi-language/locales/hi/core.json +76 -0
- package/templates/multi-language/locales/hi/kendo.json +181 -0
- package/templates/multi-language/locales/hi/template.json +169 -0
- package/templates/multi-language/locales/hi/widget.json +65 -0
- package/templates/multi-language/sample/fr.json +55 -0
- package/templates/multi-language/sample/hi.json +55 -0
- package/templates/tests/src/Module/schemas.test.ts +50 -0
- package/templates/tests-multi-language/src/multiLanguage/enText.ts +68 -0
- package/templates/tests-multi-language/src/multiLanguage/translations.test.ts +92 -0
package/README.md
CHANGED
|
@@ -1,3 +1,267 @@
|
|
|
1
|
-
#
|
|
1
|
+
# create-procode
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Scaffold a React 18 + TypeScript + Vite application wired to the four ProCode
|
|
4
|
+
low-code libraries — with `main.tsx`, all five extension points, a working sample
|
|
5
|
+
screen and the import-boundary ESLint config already in place.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm create procode@latest myapp
|
|
9
|
+
# or
|
|
10
|
+
npx create-procode myapp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# interactive — every question has a default; press Enter to accept it
|
|
17
|
+
npm create procode@latest
|
|
18
|
+
|
|
19
|
+
# name positionally, everything else prompted
|
|
20
|
+
npx create-procode myapp
|
|
21
|
+
|
|
22
|
+
# fully specified, no prompts
|
|
23
|
+
npx create-procode myapp --app-code MYAPP --base /myapp --yes
|
|
24
|
+
|
|
25
|
+
# the same through npm create — options go after `--`
|
|
26
|
+
npm create procode@latest myapp -- --app-code MYAPP --base /myapp --yes
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Options
|
|
30
|
+
|
|
31
|
+
| Option | Default | What it sets |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `<name>` / `--name <name>` | `procode-app` | Project directory and package name |
|
|
34
|
+
| `--app-code <code>` | derived from the name | `VITE_APPLICATION_CODE`, and therefore the `getAdapter<CODE>` global |
|
|
35
|
+
| `--base <path>` | `/<name>` | Vite `base` **and** `VITE_HOMEPAGE` — kept equal by construction |
|
|
36
|
+
| `--api-url <url>` | QA placeholder | `VITE_CENTRAL_SERVICE_BASE_URL`; a `{APP_CODE}` placeholder is filled in |
|
|
37
|
+
| `--auth-url <url>` | `https://localhost:7016/token` | `VITE_AUTH_URL` |
|
|
38
|
+
| `--tool-url <url>` | the low-code tool host | `VITE_TOOL_BASE_URL` |
|
|
39
|
+
| `--env <name>` | `InDraft` | `VITE_PROJECT_ENVIROMENT` — `InDraft` or `Published` |
|
|
40
|
+
| `--pm <pm>` | auto-detected | `npm` \| `pnpm` \| `yarn` \| `bun` |
|
|
41
|
+
| `--no-install` | installs | Skip the dependency install |
|
|
42
|
+
| `--no-git` | inits | Skip `git init` |
|
|
43
|
+
| `--no-sample` | includes it | Skip the sample module screen (leaves the registries empty but wired) |
|
|
44
|
+
| `--no-lint` | includes it | Skip ESLint config and its dev dependencies |
|
|
45
|
+
| `--no-tests` | includes them | Skip vitest and the wiring tests (`npm test`): every screen's slug registered and screenId unique; with multi-language, every translation key present in every language (`enText.ts` included) |
|
|
46
|
+
| `--multi-language` | asks; off with `--yes` | Switch on multi-language — a root screen with a language switch, translation files, the start-language hook ([below](#multi-language)) |
|
|
47
|
+
| `--languages <codes>` | `en,fr` | With `--multi-language` (and implies it): the languages offered, e.g. `en,fr,hi`. English is always included — it is the libraries' default text |
|
|
48
|
+
| `--no-latest` | resolves latest | Use the tested library versions in `src/deps.mjs` instead of the newest published ones |
|
|
49
|
+
| `-y, --yes` | — | Take defaults for everything not passed as a flag |
|
|
50
|
+
| `-f, --force` | — | Scaffold into a non-empty directory |
|
|
51
|
+
| `-h, --help` / `-v, --version` | — | |
|
|
52
|
+
|
|
53
|
+
Anything not passed as a flag is asked interactively, with a default already
|
|
54
|
+
filled in — so you can drive this entirely from flags, entirely from prompts, or
|
|
55
|
+
any mix. With `--yes`, or when stdin is not a TTY (CI), nothing is asked.
|
|
56
|
+
|
|
57
|
+
## What you get
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
myapp/
|
|
61
|
+
├── README.md quick start, layout, the rules, pinned versions
|
|
62
|
+
├── package.json the newest ProCode releases + the peers they declare
|
|
63
|
+
├── index.html root element + favicon link
|
|
64
|
+
├── vite.config.ts base, es2020 target, optimizeDeps
|
|
65
|
+
├── tsconfig.json / tsconfig.node.json
|
|
66
|
+
├── .env / .env.local.example every var the core reads, documented
|
|
67
|
+
├── .nvmrc Node 24
|
|
68
|
+
├── .eslintrc.cjs the framework's import boundary, at lint time
|
|
69
|
+
├── .vscode/extensions.json recommends the ESLint extension
|
|
70
|
+
├── .gitignore
|
|
71
|
+
├── public/favicon.svg served from the base path
|
|
72
|
+
└── src/
|
|
73
|
+
├── main.tsx the single integration point — every <Core> prop, typed
|
|
74
|
+
├── vite-env.d.ts typed import.meta.env
|
|
75
|
+
├── constant.ts APPLICATION_CODE, AuthToken, the three registry enums
|
|
76
|
+
├── localizationResolver.ts answers vs-template's date / currency lookups
|
|
77
|
+
├── icons.ts registerIcons map
|
|
78
|
+
├── routes.ts customRoute
|
|
79
|
+
├── Screens/ LoaderElement, ErrorElement, PageNotFoundElement
|
|
80
|
+
├── Utils/ responseFormatter, fieldErrorMapper, errorMessageMapper,
|
|
81
|
+
│ customStorages, the shared log sink
|
|
82
|
+
├── Module/ (1) slug -> schema + Sample/Contacts.json
|
|
83
|
+
├── AdapterProvider/ (2) screenId -> adapter + SampleContacts.ts (seeds the rows)
|
|
84
|
+
├── Factory/
|
|
85
|
+
│ ├── CustomWidgetFactory.tsx (3) custom widgets
|
|
86
|
+
│ ├── CustomValidationFactory.tsx (5) custom validations
|
|
87
|
+
│ ├── CustomRuleEffectFactory.ts custom rule effects (customRuleEffectProvider)
|
|
88
|
+
│ ├── RuleEffects/readOnlyEffect.ts CUS_READONLY, used by the sample
|
|
89
|
+
│ ├── Widgets/MyWidget/ a presentation-only widget, composed from library widgets
|
|
90
|
+
│ ├── Widgets/SectionNav/ the "Sections" rail for long forms
|
|
91
|
+
│ ├── Widgets/Notification/ the app's one toast host
|
|
92
|
+
│ ├── Actions/setField.ts CUS_SET_FIELD — a button reaching MODIFY_MODEL
|
|
93
|
+
│ ├── Actions/myAction.ts the custom widget's two actions
|
|
94
|
+
│ ├── Actions/sampleContacts.ts stand-ins for the API-backed standard actions
|
|
95
|
+
│ └── Validations/myValidation.ts
|
|
96
|
+
├── CustomAction.ts (4) custom actions, keyed CUS_*
|
|
97
|
+
├── CustomAuthorizationProvider.ts
|
|
98
|
+
└── assets/styles/index.scss the five-layer SCSS cascade, in order
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### The sample screen
|
|
102
|
+
|
|
103
|
+
One JSON document with no backend, built to show what the stack does declaratively:
|
|
104
|
+
|
|
105
|
+
| Coordinate | Value |
|
|
106
|
+
|---|---|
|
|
107
|
+
| slug | `sample` → `src/Module/index.ts` |
|
|
108
|
+
| schema | `src/Module/Sample/Contacts.json` |
|
|
109
|
+
| screenId | `sample-contacts` |
|
|
110
|
+
| adapter | `SampleContacts` → `src/AdapterProvider/AdapterFactory.ts` |
|
|
111
|
+
|
|
112
|
+
- `H1` heading and a permission-gated **Add contact** button (`CustomAuthorizationProvider`).
|
|
113
|
+
- Three vs-template `BLOCK` summary tiles bound to the view model.
|
|
114
|
+
- A `GRID` with sorting, column search, checkbox selection and three bulk actions
|
|
115
|
+
that open `CONFOMATIONALERTBOX` with the selection count spliced into the text;
|
|
116
|
+
the name column is a `NAVIGATIONCELL` that opens the inline editor. Sort and
|
|
117
|
+
search travel through the grid's `onScreenDataLoad` event, the same path
|
|
118
|
+
`LOAD_SCREEN_DATA` takes against a server (paging is server-side by design).
|
|
119
|
+
- An editor shown by a `ruleInterpreter` entry, with `TEXTBOX`, `DROPDOWN` (inline
|
|
120
|
+
options), `DATE`, `SWITCH`, `TEXTAREA`, the custom `MY_WIDGET` (composed from the
|
|
121
|
+
library's `TextBox` and `Button`), standard validators, `MY_VALIDATION`, and a
|
|
122
|
+
conditional *Sales region* field driven by a second rule.
|
|
123
|
+
- **Save** validates every editor field through the core's `executeValidation` seam,
|
|
124
|
+
upserts the row, refreshes the tiles and opens `INFOMATIONALERTBOX`.
|
|
125
|
+
|
|
126
|
+
The rows are seeded by the adapter's `defaultViewModel`. Five small actions in
|
|
127
|
+
`sampleContacts.ts` stand in for `DELETE_ASYNC` / `UPDATE_ASYNC` /
|
|
128
|
+
`FORMSUBMIT_CREATE_ASYNC`; each header names the standard key that replaces it,
|
|
129
|
+
and the schema's `_readme` lists the three swaps. The smoke test derives its
|
|
130
|
+
registry checks from the schema itself: every `widgetType`, action key, validation
|
|
131
|
+
name, rule target, bulk-action target and permission key in the sample is verified
|
|
132
|
+
against the registries.
|
|
133
|
+
|
|
134
|
+
Pass `--no-sample` for empty-but-wired registries instead.
|
|
135
|
+
|
|
136
|
+
### Tests and the build guard
|
|
137
|
+
|
|
138
|
+
By default the project gets **vitest** (`npm test`) with the one setting ProCode needs — `test.server.deps.inline: [/procode-/]` in `vite.config.ts`, without which a test importing a package fails to load — and `src/Module/schemas.test.ts`: every screen JSON under `src/Module` has a slug registered in `getSchemaBySlugService` (an unregistered one is a silent page-not-found) and a unique `screenId`. With multi-language it also gets `src/multiLanguage/enText.ts` (reads a reference's English back for assertions) and `translations.test.ts`: every `@t:` reference in a screen has English, every language has every key, no translation uses a `{placeholder}` its English lacks. `--no-tests` leaves all of it out.
|
|
139
|
+
|
|
140
|
+
`vite build` **refuses to run while `VITE_HARDCODED_TOKEN` is set** — it reads `.env.local` like `vite dev`, so a personal dev token would otherwise be compiled into `dist/`. `ALLOW_TOKEN_IN_BUILD=1` overrides it for a throwaway local build.
|
|
141
|
+
|
|
142
|
+
### Multi-language
|
|
143
|
+
|
|
144
|
+
Asked as *Enable multi-language?* (default **no**), then *Languages?* (default `en,fr`); or `--multi-language --languages en,fr,hi`. ProCode multi-language (core 2.6.0+) is switched on by metadata, so "yes" writes:
|
|
145
|
+
|
|
146
|
+
| File | What it is |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `src/Module/Header.json` | The root screen (`VITE_HEADER_SLUG`, loaded before any page): its `multiLanguage` block — `enabled`, `defaultLocale: "en"`, the `locales`, the `resources` (`{base}locales/{locale}/{namespace}.json`) — and a `LANGUAGESELECTOR` (segmented up to 4 languages, a menu above). Registered as the `header` slug in `src/Module/index.ts` |
|
|
149
|
+
| `public/locales/<code>/` | One folder per language: `core`, `widget`, `kendo`, `template` (the libraries' own text — empty for English, which is built in; **French and Hindi are included**, other languages show English until translated) and `header` (the language names) |
|
|
150
|
+
| `src/multiLanguage/index.ts` | `initialLocale` — the start language when the user has not chosen one (a `?lang=fr` link; extend it with a company or profile setting) |
|
|
151
|
+
| `src/main.tsx` | `multiLanguage: { initialLocale }` on the typed Core props |
|
|
152
|
+
| `src/Factory/CustomWidgetFactory.tsx` | every custom widget wrapped with `withWidgetMultiLanguage`, so a Dropdown it draws from `/widgets` and Kendo's own text follow the language |
|
|
153
|
+
| the sample screen | shows the header (`enableHeader: true`); after the install, `procode-extract-text` moves its text into `public/locales/en/sample.json`, and French and Hindi translations of it are written. With `--no-install` the command is printed instead |
|
|
154
|
+
| `README.md` | a *Multi-language* section: where everything lives, how to convert a screen, add a language, translate in code |
|
|
155
|
+
|
|
156
|
+
The generated README carries the rest. The smoke test scaffolds a multi-language app too and checks every file, key and wiring point above.
|
|
157
|
+
|
|
158
|
+
### What the generated project encodes
|
|
159
|
+
|
|
160
|
+
These failure modes are prevented by construction rather than left to memory:
|
|
161
|
+
|
|
162
|
+
- **The newest ProCode libraries, with matching peers.** At scaffold time the CLI
|
|
163
|
+
asks the npm registry for the latest version of each of the four packages, and
|
|
164
|
+
writes the React, Kendo, router and axios ranges *those* versions declare as
|
|
165
|
+
peers. `@progress/kendo-theme-default` and `kendo-react-labels` follow the
|
|
166
|
+
kendo-react range, and `@types/react` follows the React major. A library
|
|
167
|
+
release reaches new apps with no generator update. It uses the registry npx
|
|
168
|
+
was run with, so a mirror works. If the lookup fails (offline, a timeout), the
|
|
169
|
+
CLI warns and uses the tested set in `src/deps.mjs`; `--no-latest` chooses
|
|
170
|
+
that set on purpose.
|
|
171
|
+
- **React stays on the major the libraries declare.** Today that is
|
|
172
|
+
`react ^18.2.0`; 19 installs cleanly then fails at runtime.
|
|
173
|
+
- **`react-redux` is never declared.** It is bundled inside the core. Declaring it
|
|
174
|
+
and floating to 9.x gives two Provider contexts, and every screen renders blank
|
|
175
|
+
with nothing in the console.
|
|
176
|
+
- **`@progress/kendo-theme-default` is included.** It is a missing peer
|
|
177
|
+
declaration, not an optional extra — the widget library's SCSS imports it by
|
|
178
|
+
name, so omitting it is a hard build failure on the first `npm run dev`.
|
|
179
|
+
- **`build.target: es2020`.** At `es2015` the downlevel transform miscompiles a
|
|
180
|
+
`const` declaring two rest patterns at once, and Kendo's date picker throws
|
|
181
|
+
*"Cannot destructure 'null' as it is null"* in built bundles only.
|
|
182
|
+
- **`base` and `VITE_HOMEPAGE` are written from one value**, so they cannot drift.
|
|
183
|
+
- **`VITE_APPLICATION_CODE` is validated** as a JS identifier fragment, because it
|
|
184
|
+
becomes the `getAdapter<CODE>` global the core resolves by name.
|
|
185
|
+
- **The SCSS import order** is fixed and annotated — it is the cascade, and
|
|
186
|
+
reordering gives "Undefined variable" or silently wrong spacing.
|
|
187
|
+
- **`main.tsx` ordering** is annotated as load-bearing, with `validateStatus`
|
|
188
|
+
first, and the smoke test asserts it.
|
|
189
|
+
- **Every `<Core>` seam is wired, and type-checked.** `<Core>` accepts `any`, so a
|
|
190
|
+
misspelled prop compiles and is silently dropped. `main.tsx` builds a typed
|
|
191
|
+
`CoreProps` object instead, and the smoke test asserts every seam is in it:
|
|
192
|
+
widget, action, validation and rule-effect providers, authorization, routes,
|
|
193
|
+
storages, response formatting, 422 field errors, error-message text, the 401
|
|
194
|
+
handler, the loader / error / 404 screens and the notification host. The core
|
|
195
|
+
and widget loggers, `registerIcons` and `registerLocalization` are registered
|
|
196
|
+
before render. Deprecated props are never emitted.
|
|
197
|
+
- **Rule effects are injectable.** `CustomRuleEffectFactory` is resolved before the
|
|
198
|
+
standard effects, so it can add effects or override `SHOW` / `HIDE`. The sample
|
|
199
|
+
uses its `CUS_READONLY`, and the smoke test checks every effect a schema names is
|
|
200
|
+
standard or registered.
|
|
201
|
+
- **Factories use `Map` lookups.** On a plain object, a key like `"constructor"`
|
|
202
|
+
returns an `Object.prototype` function the core would invoke.
|
|
203
|
+
- **The core stylesheet builds on Linux.** procode-lowcode-core exports `./styles`
|
|
204
|
+
as `src/Assets/…`, but ships `src/assets/…`. A Vite alias points the import at the
|
|
205
|
+
real path, so CI and Docker builds stop failing with "Can't find stylesheet to import".
|
|
206
|
+
- **The localization seam is registered.** `main.tsx` calls vs-template's
|
|
207
|
+
`registerLocalization()` with `src/localizationResolver.ts` before render. It
|
|
208
|
+
answers every `LocalizationKey` from one date pattern, including
|
|
209
|
+
`dateFormatSections`. Without that key, a date box's placeholder changes to
|
|
210
|
+
Kendo's long form on focus.
|
|
211
|
+
- **The framework's import boundary is enforced at lint time.** Custom code is still
|
|
212
|
+
built from library parts: a control
|
|
213
|
+
inside a custom widget is the library's `TextBox` / `Dropdown` / `Button` from
|
|
214
|
+
`procode-lowcode-kendo-widget/widgets`, never `@progress/kendo-react-*`; icons go
|
|
215
|
+
through `loadIcon` / `registerIcons`, never a second icon set; navigation is
|
|
216
|
+
`NAVIGATE` / `MENUNAVIGATE`, never `window.location`; toasts are `responseConfig`
|
|
217
|
+
or the one Notification host, never `react-toastify` elsewhere; `react-redux`,
|
|
218
|
+
`react-router-dom` and `procode-*/src/…` deep paths are library internals.
|
|
219
|
+
`.eslintrc.cjs` refuses any of those. It honours one escape hatch — a
|
|
220
|
+
`// procode-escalation: <key> — L<n> cannot compose this because <limitation>` comment — for a control
|
|
221
|
+
the library genuinely does not wrap, so every exception is greppable. The sample
|
|
222
|
+
`MyWidget` is itself composed from library widgets, so the first custom widget a
|
|
223
|
+
developer copies is a correct one.
|
|
224
|
+
|
|
225
|
+
## Maintenance
|
|
226
|
+
|
|
227
|
+
### Library versions
|
|
228
|
+
|
|
229
|
+
New apps get the latest ProCode releases automatically, so a library release
|
|
230
|
+
needs no generator change. [`src/deps.mjs`](./src/deps.mjs) holds two things that
|
|
231
|
+
still need care:
|
|
232
|
+
|
|
233
|
+
- **The tested fallback set**: used with `--no-latest` or when the registry is
|
|
234
|
+
unreachable. Refresh it after a library release has been verified, then run the
|
|
235
|
+
smoke test with `--install`. Current pins: core `^2.7.0`, kendo-widget `^3.2.0`,
|
|
236
|
+
vs-template `^2.15.1`, vs-theme `^2.7.1` (the latest releases as of 2026-11-06), on KendoReact `^16.2.0` with
|
|
237
|
+
kendo-theme-default `^14.7.0`.
|
|
238
|
+
- **`UNDECLARED_BUT_REQUIRED`**: packages the libraries need but do not declare
|
|
239
|
+
as peers, so the lookup cannot find them. Add one here if a release starts
|
|
240
|
+
needing a new one.
|
|
241
|
+
|
|
242
|
+
The generated README's version table is rendered from whichever set was used.
|
|
243
|
+
|
|
244
|
+
### Test
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npm run smoke # scaffold + structural assertions, seconds
|
|
248
|
+
node scripts/smoke-test.mjs --install # also npm install + npm run build
|
|
249
|
+
node scripts/smoke-test.mjs --keep # leave the generated app for inspection
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The `--install` run is the only one that catches a dependency set that resolves on
|
|
253
|
+
paper but not in practice.
|
|
254
|
+
|
|
255
|
+
## Publishing
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npm version patch
|
|
259
|
+
npm publish
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`files` in `package.json` ships `bin/`, `src/` and `templates/` — about 50 KB packed.
|
|
263
|
+
|
|
264
|
+
## Requirements
|
|
265
|
+
|
|
266
|
+
Node 18+ to run the generator. Node 24 recommended for the generated app.
|
|
267
|
+
Zero runtime dependencies — the CLI uses only Node builtins.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { run } from "../src/cli.mjs";
|
|
3
|
+
|
|
4
|
+
run(process.argv.slice(2)).catch((err) => {
|
|
5
|
+
// eslint-disable-next-line no-console
|
|
6
|
+
console.error("\n\x1b[31m✖\x1b[0m " + (err?.message ?? err));
|
|
7
|
+
if (process.env.PROCODE_DEBUG) console.error(err);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-procode",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Scaffold a React + TypeScript + Vite app wired to the four ProCode low-code libraries (core, kendo-widget, vs-template, vs-theme).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"private": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=18.0.0"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"create-procode": "bin/create-procode.mjs"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin",
|
|
16
|
+
"src",
|
|
17
|
+
"templates",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"smoke": "node scripts/smoke-test.mjs"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"procode",
|
|
25
|
+
"lowcode",
|
|
26
|
+
"varstreet",
|
|
27
|
+
"vite",
|
|
28
|
+
"react",
|
|
29
|
+
"scaffold",
|
|
30
|
+
"create-app"
|
|
31
|
+
]
|
|
32
|
+
}
|
package/src/args.mjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flag table. `type` drives parsing; `value` on a boolean flag is what the
|
|
3
|
+
* long form sets, so `--install` and `--no-install` share one key.
|
|
4
|
+
*/
|
|
5
|
+
export const FLAGS = {
|
|
6
|
+
name: { type: "string", arg: "<name>", desc: "Project directory and package name (or pass it positionally)" },
|
|
7
|
+
"app-code": { type: "string", arg: "<code>", desc: "VITE_APPLICATION_CODE — must match the getAdapter<CODE> global" },
|
|
8
|
+
base: { type: "string", arg: "<path>", desc: "Vite base / VITE_HOMEPAGE, e.g. /myapp" },
|
|
9
|
+
"api-url": { type: "string", arg: "<url>", desc: "VITE_CENTRAL_SERVICE_BASE_URL (the {APP_CODE} placeholder is filled in)" },
|
|
10
|
+
"auth-url": { type: "string", arg: "<url>", desc: "VITE_AUTH_URL" },
|
|
11
|
+
"tool-url": { type: "string", arg: "<url>", desc: "VITE_TOOL_BASE_URL" },
|
|
12
|
+
env: { type: "string", arg: "<name>", desc: "VITE_PROJECT_ENVIROMENT (InDraft | Published)", choices: ["InDraft", "Published"] },
|
|
13
|
+
pm: { type: "string", arg: "<pm>", desc: "Package manager: npm | pnpm | yarn | bun", choices: ["npm", "pnpm", "yarn", "bun"] },
|
|
14
|
+
install: { type: "boolean", value: true, desc: "Run the package manager install (--no-install to skip)" },
|
|
15
|
+
git: { type: "boolean", value: true, desc: "Run git init (--no-git to skip)" },
|
|
16
|
+
sample: { type: "boolean", value: true, desc: "Include the Sample module screen (--no-sample to skip)" },
|
|
17
|
+
lint: { type: "boolean", value: true, desc: "Include ESLint config and dev dependencies" },
|
|
18
|
+
tests: { type: "boolean", value: true, desc: "Include vitest and the wiring tests — screen registration, and with multi-language, translation completeness (--no-tests to skip)" },
|
|
19
|
+
"multi-language": { type: "boolean", value: true, desc: "Switch on multi-language: root screen, language selector, translation files (--no-multi-language to skip)" },
|
|
20
|
+
languages: { type: "string", arg: "<codes>", desc: "With --multi-language: the languages offered, e.g. en,fr,hi (English is always included; default en,fr)" },
|
|
21
|
+
latest: { type: "boolean", value: true, desc: "Use the newest published ProCode libraries and their peers (--no-latest: the tested versions)" },
|
|
22
|
+
yes: { type: "boolean", value: true, short: "y", desc: "Accept defaults for everything not passed as a flag" },
|
|
23
|
+
force: { type: "boolean", value: true, short: "f", desc: "Scaffold into a non-empty directory" },
|
|
24
|
+
help: { type: "boolean", value: true, short: "h", desc: "Show this help" },
|
|
25
|
+
version: { type: "boolean", value: true, short: "v", desc: "Print the version" },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const ALIASES = Object.fromEntries(
|
|
29
|
+
Object.entries(FLAGS)
|
|
30
|
+
.filter(([, f]) => f.short)
|
|
31
|
+
.map(([k, f]) => [f.short, k]),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export function parseArgs(argv) {
|
|
35
|
+
const opts = {};
|
|
36
|
+
const positionals = [];
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < argv.length; i++) {
|
|
39
|
+
const token = argv[i];
|
|
40
|
+
|
|
41
|
+
if (token === "--") {
|
|
42
|
+
positionals.push(...argv.slice(i + 1));
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (token.startsWith("--")) {
|
|
47
|
+
let body = token.slice(2);
|
|
48
|
+
let inlineValue;
|
|
49
|
+
const eq = body.indexOf("=");
|
|
50
|
+
if (eq !== -1) {
|
|
51
|
+
inlineValue = body.slice(eq + 1);
|
|
52
|
+
body = body.slice(0, eq);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let negated = false;
|
|
56
|
+
let key = body;
|
|
57
|
+
if (body.startsWith("no-") && FLAGS[body.slice(3)]?.type === "boolean") {
|
|
58
|
+
negated = true;
|
|
59
|
+
key = body.slice(3);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const flag = FLAGS[key];
|
|
63
|
+
if (!flag) throw new Error(`Unknown option --${body}. Run with --help to see the supported options.`);
|
|
64
|
+
|
|
65
|
+
if (flag.type === "boolean") {
|
|
66
|
+
if (inlineValue !== undefined) {
|
|
67
|
+
opts[key] = !["false", "0", "no"].includes(inlineValue.toLowerCase());
|
|
68
|
+
} else {
|
|
69
|
+
opts[key] = negated ? !flag.value : flag.value;
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const value = inlineValue !== undefined ? inlineValue : argv[++i];
|
|
75
|
+
if (value === undefined || value.startsWith("--")) {
|
|
76
|
+
throw new Error(`Option --${key} needs a value, e.g. --${key} ${flag.arg}`);
|
|
77
|
+
}
|
|
78
|
+
if (flag.choices && !flag.choices.includes(value)) {
|
|
79
|
+
throw new Error(`--${key} must be one of: ${flag.choices.join(", ")} (got "${value}")`);
|
|
80
|
+
}
|
|
81
|
+
opts[key] = value;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (token.startsWith("-") && token.length > 1) {
|
|
86
|
+
for (const ch of token.slice(1)) {
|
|
87
|
+
const key = ALIASES[ch];
|
|
88
|
+
if (!key) throw new Error(`Unknown option -${ch}. Run with --help to see the supported options.`);
|
|
89
|
+
opts[key] = FLAGS[key].value ?? true;
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
positionals.push(token);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { opts, positionals };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function helpText(version) {
|
|
101
|
+
const rows = Object.entries(FLAGS).map(([key, f]) => {
|
|
102
|
+
const short = f.short ? `-${f.short}, ` : " ";
|
|
103
|
+
const left = ` ${short}--${key}${f.arg ? " " + f.arg : ""}`;
|
|
104
|
+
return { left, desc: f.desc, choices: f.choices };
|
|
105
|
+
});
|
|
106
|
+
const width = Math.max(...rows.map((r) => r.left.length)) + 2;
|
|
107
|
+
|
|
108
|
+
return `
|
|
109
|
+
create-procode v${version}
|
|
110
|
+
|
|
111
|
+
Scaffold a React + TypeScript + Vite app wired to the four ProCode libraries:
|
|
112
|
+
procode-lowcode-core, procode-lowcode-kendo-widget, procode-vs-template and
|
|
113
|
+
procode-vs-theme — with main.tsx, the five extension points, a sample module
|
|
114
|
+
screen and the import-boundary ESLint config already in place.
|
|
115
|
+
|
|
116
|
+
USAGE
|
|
117
|
+
npm create procode@latest <project-name> -- [options]
|
|
118
|
+
npx create-procode <project-name> [options]
|
|
119
|
+
npx create-procode --name <project-name> [options]
|
|
120
|
+
|
|
121
|
+
OPTIONS
|
|
122
|
+
${rows.map((r) => r.left.padEnd(width) + r.desc + (r.choices ? ` [${r.choices.join("|")}]` : "")).join("\n")}
|
|
123
|
+
|
|
124
|
+
EXAMPLES
|
|
125
|
+
# interactive
|
|
126
|
+
npx create-procode
|
|
127
|
+
|
|
128
|
+
# everything on the command line, no prompts
|
|
129
|
+
npx create-procode myapp --app-code MYAPP --base /myapp --yes
|
|
130
|
+
|
|
131
|
+
# with multi-language: English, French and Hindi, a language switch in the header
|
|
132
|
+
npx create-procode myapp --multi-language --languages en,fr,hi --yes
|
|
133
|
+
|
|
134
|
+
# match an existing deployment, skip install
|
|
135
|
+
npx create-procode vsbackoffice-next \
|
|
136
|
+
--app-code LCBOV2 --base /lcbov2 \
|
|
137
|
+
--api-url https://qa.example.com/lowcodev2/api/{APP_CODE}/ \
|
|
138
|
+
--no-install --yes
|
|
139
|
+
|
|
140
|
+
AFTER SCAFFOLDING
|
|
141
|
+
cd <project-name>
|
|
142
|
+
npm install # if you passed --no-install
|
|
143
|
+
npm run dev # http://localhost:5173<base>/sample
|
|
144
|
+
`;
|
|
145
|
+
}
|