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.
Files changed (68) hide show
  1. package/README.md +266 -2
  2. package/bin/create-procode.mjs +9 -0
  3. package/package.json +30 -4
  4. package/src/args.mjs +145 -0
  5. package/src/cli.mjs +514 -0
  6. package/src/deps.mjs +208 -0
  7. package/src/log.mjs +23 -0
  8. package/src/multiLanguage.mjs +409 -0
  9. package/src/prompt.mjs +60 -0
  10. package/src/scaffold.mjs +104 -0
  11. package/templates/base/README.md.tmpl +185 -0
  12. package/templates/base/_dotenv.local.example.tmpl +8 -0
  13. package/templates/base/_dotenv.tmpl +41 -0
  14. package/templates/base/_doteslintrc.cjs.tmpl +125 -0
  15. package/templates/base/_dotgitignore.tmpl +32 -0
  16. package/templates/base/_dotnvmrc.tmpl +1 -0
  17. package/templates/base/_dotvscode/extensions.json +3 -0
  18. package/templates/base/index.html.tmpl +18 -0
  19. package/templates/base/package.json.tmpl +15 -0
  20. package/templates/base/public/favicon.svg +4 -0
  21. package/templates/base/src/AdapterProvider/AdapterFactory.ts.tmpl +21 -0
  22. package/templates/base/src/AdapterProvider/SampleContacts.ts.tmpl +136 -0
  23. package/templates/base/src/CustomAction.ts.tmpl +47 -0
  24. package/templates/base/src/CustomAuthorizationProvider.ts.tmpl +77 -0
  25. package/templates/base/src/Factory/Actions/myAction.ts.tmpl +51 -0
  26. package/templates/base/src/Factory/Actions/sampleContacts.ts.tmpl +244 -0
  27. package/templates/base/src/Factory/Actions/setField.ts.tmpl +20 -0
  28. package/templates/base/src/Factory/CustomRuleEffectFactory.ts.tmpl +30 -0
  29. package/templates/base/src/Factory/CustomValidationFactory.tsx.tmpl +24 -0
  30. package/templates/base/src/Factory/CustomWidgetFactory.tsx.tmpl +43 -0
  31. package/templates/base/src/Factory/RuleEffects/readOnlyEffect.ts.tmpl +27 -0
  32. package/templates/base/src/Factory/Validations/myValidation.ts.tmpl +52 -0
  33. package/templates/base/src/Factory/Widgets/MyWidget/index.tsx.tmpl +101 -0
  34. package/templates/base/src/Factory/Widgets/Notification/index.tsx.tmpl +53 -0
  35. package/templates/base/src/Factory/Widgets/Notification/notifiFunc.ts.tmpl +9 -0
  36. package/templates/base/src/Factory/Widgets/SectionNav/index.tsx +193 -0
  37. package/templates/base/src/Factory/Widgets/SectionNav/style.scss +139 -0
  38. package/templates/base/src/Module/Sample/Contacts.json.tmpl +965 -0
  39. package/templates/base/src/Module/index.ts.tmpl +26 -0
  40. package/templates/base/src/Screens/index.tsx.tmpl +45 -0
  41. package/templates/base/src/Utils/errorMessages.ts.tmpl +35 -0
  42. package/templates/base/src/Utils/fieldErrorMapper.ts.tmpl +46 -0
  43. package/templates/base/src/Utils/logger.ts.tmpl +21 -0
  44. package/templates/base/src/Utils/responseFormatter.ts.tmpl +39 -0
  45. package/templates/base/src/Utils/storages.ts.tmpl +35 -0
  46. package/templates/base/src/assets/styles/index.scss.tmpl +149 -0
  47. package/templates/base/src/constant.ts.tmpl +53 -0
  48. package/templates/base/src/icons.ts.tmpl +17 -0
  49. package/templates/base/src/localizationResolver.ts.tmpl +48 -0
  50. package/templates/base/src/main.tsx.tmpl +136 -0
  51. package/templates/base/src/routes.ts.tmpl +20 -0
  52. package/templates/base/src/vite-env.d.ts.tmpl +20 -0
  53. package/templates/base/tsconfig.json.tmpl +30 -0
  54. package/templates/base/tsconfig.node.json.tmpl +11 -0
  55. package/templates/base/vite.config.ts.tmpl +90 -0
  56. package/templates/multi-language/locales/fr/core.json +76 -0
  57. package/templates/multi-language/locales/fr/kendo.json +181 -0
  58. package/templates/multi-language/locales/fr/template.json +169 -0
  59. package/templates/multi-language/locales/fr/widget.json +65 -0
  60. package/templates/multi-language/locales/hi/core.json +76 -0
  61. package/templates/multi-language/locales/hi/kendo.json +181 -0
  62. package/templates/multi-language/locales/hi/template.json +169 -0
  63. package/templates/multi-language/locales/hi/widget.json +65 -0
  64. package/templates/multi-language/sample/fr.json +55 -0
  65. package/templates/multi-language/sample/hi.json +55 -0
  66. package/templates/tests/src/Module/schemas.test.ts +50 -0
  67. package/templates/tests-multi-language/src/multiLanguage/enText.ts +68 -0
  68. package/templates/tests-multi-language/src/multiLanguage/translations.test.ts +92 -0
package/README.md CHANGED
@@ -1,3 +1,267 @@
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-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": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }