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