@genesislcap/blank-app-seed 5.22.1 → 5.23.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 (191) hide show
  1. package/.genx/configure.js +11 -1
  2. package/.genx/package.json +1 -1
  3. package/.genx/scripts/generate-test-apps.sh +124 -0
  4. package/.genx/templates/angular/chart.hbs +19 -7
  5. package/.genx/templates/angular/component/component.column.defs.hbs +16 -4
  6. package/.genx/templates/angular/component/component.create.form.hbs +10 -2
  7. package/.genx/templates/angular/component/component.events.config.hbs +31 -6
  8. package/.genx/templates/angular/component/component.filter.form.hbs +1 -2
  9. package/.genx/templates/angular/component/component.gridOptions.hbs +15 -3
  10. package/.genx/templates/angular/component/component.hbs +77 -46
  11. package/.genx/templates/angular/component/component.index.hbs +1 -1
  12. package/.genx/templates/angular/component/component.update.form.hbs +1 -1
  13. package/.genx/templates/angular/entityManager.hbs +6 -6
  14. package/{client-tmp/angular/src/app/utils/fdc3.ts → .genx/templates/angular/fdc3.ts.hbs} +6 -8
  15. package/.genx/templates/angular/form.hbs +9 -12
  16. package/.genx/templates/angular/grid.hbs +43 -40
  17. package/.genx/templates/angular/route.hbs +8 -2
  18. package/.genx/templates/angular/route.template.hbs +7 -7
  19. package/.genx/templates/angular/slices/eventing.slice.hbs +21 -10
  20. package/.genx/templates/angular/store.hbs +1 -5
  21. package/.genx/templates/angular/tabsPanel.hbs +2 -2
  22. package/.genx/templates/react/chart.hbs +6 -15
  23. package/.genx/templates/react/component/component.column.defs.hbs +16 -10
  24. package/.genx/templates/react/component/component.create.form.hbs +2 -2
  25. package/.genx/templates/react/component/component.events.config.hbs +32 -5
  26. package/.genx/templates/react/component/component.filter.form.hbs +1 -2
  27. package/.genx/templates/react/component/component.gridOptions.hbs +17 -11
  28. package/.genx/templates/react/component/component.hbs +84 -42
  29. package/.genx/templates/react/component/component.update.form.hbs +1 -1
  30. package/.genx/templates/react/entityManager.hbs +8 -16
  31. package/{client-tmp/react/src/utils/fdc3.ts → .genx/templates/react/fdc3.ts.hbs} +7 -8
  32. package/.genx/templates/react/form.hbs +1 -15
  33. package/.genx/templates/react/grid.hbs +11 -11
  34. package/.genx/templates/react/gridLayout.hbs +44 -4
  35. package/.genx/templates/react/horizontalLayout.hbs +10 -1
  36. package/.genx/templates/react/route.hbs +21 -14
  37. package/.genx/templates/react/slices/eventing.slice.hbs +19 -8
  38. package/.genx/templates/react/store.hbs +1 -5
  39. package/.genx/templates/react/tabsLayout.hbs +5 -1
  40. package/.genx/templates/web-components/chart.hbs +25 -25
  41. package/.genx/templates/web-components/component/component.column.defs.hbs +11 -3
  42. package/.genx/templates/web-components/component/component.create.form.hbs +11 -4
  43. package/.genx/templates/web-components/component/component.events.config.hbs +33 -6
  44. package/.genx/templates/web-components/component/component.filter.form.hbs +1 -2
  45. package/.genx/templates/web-components/component/component.gridOptions.hbs +12 -3
  46. package/.genx/templates/web-components/component/component.hbs +19 -20
  47. package/.genx/templates/web-components/component/component.index.hbs +1 -1
  48. package/.genx/templates/web-components/component/component.template.hbs +9 -13
  49. package/.genx/templates/web-components/component/component.update.form.hbs +1 -1
  50. package/.genx/templates/web-components/entityManager.hbs +92 -73
  51. package/{client-tmp/web-components/src/utils/fdc3.ts → .genx/templates/web-components/fdc3.ts.hbs} +6 -7
  52. package/.genx/templates/web-components/form.hbs +16 -15
  53. package/.genx/templates/web-components/grid.hbs +49 -41
  54. package/.genx/templates/web-components/gridLayout.hbs +32 -28
  55. package/.genx/templates/web-components/horizontalLayout.hbs +5 -5
  56. package/.genx/templates/web-components/route.template.hbs +4 -6
  57. package/.genx/templates/web-components/slices/eventing.slice.hbs +23 -15
  58. package/.genx/templates/web-components/store.hbs +1 -9
  59. package/.genx/templates/web-components/tabsLayout.hbs +5 -5
  60. package/.genx/tests/fixtures/routes-full.json +299 -0
  61. package/.genx/tests/fixtures/testapp/client/src/pages/Home/Home.tsx +12 -0
  62. package/.genx/utils/formatJSONValue.js +38 -2
  63. package/.genx/utils/formatRouteData.js +67 -18
  64. package/.genx/utils/generateTile.js +10 -1
  65. package/.genx/utils/gridSerializers.js +63 -23
  66. package/.genx/utils/registerPartials.js +15 -12
  67. package/.genx/utils/toJsStringLiteral.js +37 -0
  68. package/.github/workflows/build.yml +22 -1
  69. package/.github/workflows/release.yaml +96 -0
  70. package/.husky/pre-commit +8 -0
  71. package/.releaserc +1 -1
  72. package/CHANGELOG.md +23 -0
  73. package/client-tmp/angular/.oxfmtrc.json +32 -0
  74. package/client-tmp/angular/.oxlintrc.json +4 -0
  75. package/client-tmp/angular/README.md +4 -4
  76. package/client-tmp/angular/jest.config.js +1 -1
  77. package/client-tmp/angular/lint-css.js +24 -3
  78. package/client-tmp/angular/oxlint-staged.js +45 -0
  79. package/client-tmp/angular/package.json +19 -9
  80. package/client-tmp/angular/playwright.config.ts +12 -11
  81. package/client-tmp/angular/src/app/app.component.html +0 -1
  82. package/client-tmp/angular/src/app/app.component.ts +8 -9
  83. package/client-tmp/angular/src/app/app.config.ts +2 -2
  84. package/client-tmp/angular/src/app/app.module.ts +6 -5
  85. package/client-tmp/angular/src/app/components/error-message/error-message.component.html +29 -15
  86. package/client-tmp/angular/src/app/components/error-message/error-message.component.spec.ts +2 -2
  87. package/client-tmp/angular/src/app/components/error-message/error-message.component.ts +3 -5
  88. package/client-tmp/angular/src/app/directive/app-lazy-load.directive.ts +1 -0
  89. package/client-tmp/angular/src/app/guards/chained.guard.ts +19 -19
  90. package/client-tmp/angular/src/app/guards/connection.guard.ts +12 -12
  91. package/client-tmp/angular/src/app/layouts/blank/blank.layout.spec.ts +1 -1
  92. package/client-tmp/angular/src/app/layouts/blank/blank.layout.ts +1 -5
  93. package/client-tmp/angular/src/app/layouts/default/default.layout.html +3 -4
  94. package/client-tmp/angular/src/app/layouts/default/default.layout.spec.ts +4 -4
  95. package/client-tmp/angular/src/app/layouts/default/default.layout.ts +1 -1
  96. package/client-tmp/angular/src/app/pages/auth-login/auth-login.component.ts +1 -2
  97. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.html +3 -3
  98. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.scss +1 -1
  99. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.spec.ts +5 -5
  100. package/client-tmp/angular/src/app/pages/not-permitted/not-permitted.component.ts +3 -4
  101. package/client-tmp/angular/src/app/services/route.service.ts +79 -74
  102. package/client-tmp/angular/src/app/share/foundation-auth.ts +7 -1
  103. package/client-tmp/angular/src/app/share/genesis-components.ts +66 -68
  104. package/client-tmp/angular/src/app/utils/index.ts +3 -1
  105. package/client-tmp/angular/src/app/utils/permissions.ts +7 -7
  106. package/client-tmp/angular/src/declaration.d.ts +1 -1
  107. package/client-tmp/angular/src/environments/environment.ts +1 -1
  108. package/client-tmp/angular/src/jest.setup.ts +8 -0
  109. package/client-tmp/angular/src/main.ts +2 -2
  110. package/client-tmp/angular/src/pbc/container.ts +28 -26
  111. package/client-tmp/angular/src/pbc/elementsRenderer.ts +49 -39
  112. package/client-tmp/angular/src/pbc/utils.ts +61 -60
  113. package/client-tmp/angular/src/styles/_mixins.scss +7 -8
  114. package/client-tmp/angular/src/styles/styles.css +6 -6
  115. package/client-tmp/angular/test/e2e/fixture.ts +2 -2
  116. package/client-tmp/angular/test/e2e/flows/001-protected.e2e.ts +1 -1
  117. package/client-tmp/angular/tsconfig.app.json +2 -8
  118. package/client-tmp/angular/tsconfig.json +1 -4
  119. package/client-tmp/angular/tsconfig.spec.json +4 -11
  120. package/client-tmp/angular/webpack.dev.config.js +3 -4
  121. package/client-tmp/angular/webpack.prod.config.js +3 -4
  122. package/client-tmp/angular/webpack.shared.config.js +20 -24
  123. package/client-tmp/react/.oxfmtrc.json +30 -0
  124. package/client-tmp/react/.oxlintrc.json +4 -0
  125. package/client-tmp/react/README.md +4 -4
  126. package/client-tmp/react/jest.config.ts +8 -4
  127. package/client-tmp/react/jest.setup.ts +85 -1
  128. package/client-tmp/react/oxlint-staged.js +45 -0
  129. package/client-tmp/react/package.json +24 -9
  130. package/client-tmp/react/playwright.config.ts +11 -10
  131. package/client-tmp/react/src/App.tsx +27 -29
  132. package/client-tmp/react/src/__mocks__/fileMock.js +1 -0
  133. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.test.tsx +16 -4
  134. package/client-tmp/react/src/components/ErrorMessage/ErrorMessage.tsx +32 -29
  135. package/client-tmp/react/src/components/error-boundary/ErrorBoundary.tsx +15 -6
  136. package/client-tmp/react/src/components/routes/AppRoutes.tsx +1 -1
  137. package/client-tmp/react/src/components/single-component/registry.ts +4 -0
  138. package/client-tmp/react/src/custom-elements.d.ts +4 -2
  139. package/client-tmp/react/src/helpers/hasPermissionHelper.tsx +1 -1
  140. package/client-tmp/react/src/helpers/isAuthenticatedHelper.tsx +1 -1
  141. package/client-tmp/react/src/helpers/isConnectedHelper.tsx +3 -3
  142. package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +7 -11
  143. package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +8 -8
  144. package/client-tmp/react/src/main.tsx +11 -11
  145. package/client-tmp/react/src/pages/AuthPage/AuthPage.test.tsx +13 -2
  146. package/client-tmp/react/src/pages/AuthPage/AuthPage.tsx +14 -13
  147. package/client-tmp/react/src/pages/NotFoundPage/NotFoundPage.tsx +2 -7
  148. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +8 -4
  149. package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +1 -1
  150. package/client-tmp/react/src/pbc/container.tsx +9 -4
  151. package/client-tmp/react/src/pbc/elementsRenderer.tsx +13 -7
  152. package/client-tmp/react/src/pbc/utils.ts +60 -59
  153. package/client-tmp/react/src/services/auth.service.ts +1 -1
  154. package/client-tmp/react/src/services/store.service.ts +5 -3
  155. package/client-tmp/react/src/share/foundation-login.ts +2 -2
  156. package/client-tmp/react/src/share/genesis-components.ts +46 -47
  157. package/client-tmp/react/src/store/AuthContext.tsx +2 -10
  158. package/client-tmp/react/src/store/RoutesContext.test.tsx +99 -0
  159. package/client-tmp/react/src/store/RoutesContext.tsx +25 -5
  160. package/client-tmp/react/src/styles/styles.css +7 -7
  161. package/client-tmp/react/src/types/LayoutName.ts +1 -1
  162. package/client-tmp/react/src/utils/getLayoutNameByRoute.ts +1 -1
  163. package/client-tmp/react/src/utils/index.ts +2 -0
  164. package/client-tmp/react/src/utils/layout.ts +8 -1
  165. package/client-tmp/react/src/utils/permissions.ts +7 -7
  166. package/client-tmp/react/src/utils/useDocumentTitle.ts +5 -6
  167. package/client-tmp/react/test/e2e/fixture.ts +5 -2
  168. package/client-tmp/react/test/e2e/flows/001-protected.e2e.ts +3 -1
  169. package/client-tmp/react/tsconfig.app.json +1 -1
  170. package/client-tmp/react/tsconfig.json +1 -5
  171. package/client-tmp/web-components/.oxfmtrc.json +30 -0
  172. package/client-tmp/web-components/.oxlintrc.json +4 -0
  173. package/client-tmp/web-components/oxlint-staged.js +45 -0
  174. package/client-tmp/web-components/package.json +14 -22
  175. package/client-tmp/web-components/src/components/components.ts +9 -7
  176. package/client-tmp/web-components/src/main/main.css +1 -2
  177. package/client-tmp/web-components/src/main/main.ts +1 -1
  178. package/client-tmp/web-components/src/routes/config.ts +4 -0
  179. package/client-tmp/web-components/src/utils/index.ts +2 -0
  180. package/client-tmp/web-components/tsconfig.json +1 -4
  181. package/package.json +1 -1
  182. package/.genx/templates/angular/gridLayout.hbs +0 -30
  183. package/.genx/templates/angular/horizontalLayout.hbs +0 -7
  184. package/.genx/templates/angular/tabsLayout.hbs +0 -7
  185. package/.genx/tests/fixtures/testapp/client/src/routes/home/home.template.ts +0 -6
  186. package/.github/workflows/release.yml +0 -50
  187. package/client-tmp/react/.babelrc +0 -10
  188. package/client-tmp/react/.eslintrc.cjs +0 -21
  189. package/client-tmp/react/git +0 -0
  190. package/client-tmp/react/webpack.config.js +0 -121
  191. package/client-tmp/web-components/.prettierignore +0 -14
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Renders a value as a JS/TS string literal, quotes included, in the exact shape
3
+ * oxfmt (`singleQuote: true`, prettier semantics) would print it — so generated
4
+ * code passes `oxfmt --check` without a reformat pass.
5
+ *
6
+ * Two things have to be right:
7
+ *
8
+ * 1. Escaping. Naively escaping only apostrophes breaks on backslashes
9
+ * (`back\` swallows the closing quote) and on newlines/tabs/control
10
+ * characters (an unterminated literal). `JSON.stringify` already escapes
11
+ * `\`, `"` and every control character exactly the way oxfmt prints them.
12
+ * 2. Quote choice. oxfmt prefers single quotes but flips to double quotes when
13
+ * the content contains strictly more apostrophes than double quotes (fewer
14
+ * escapes wins). Emitting `'Trader\'s Book'` would therefore be rewritten to
15
+ * `"Trader's Book"` by oxfmt and fail `--check`.
16
+ *
17
+ * @param {unknown} value value to render (coerced with String())
18
+ * @returns {string} the literal, including its enclosing quotes
19
+ */
20
+ const toJsStringLiteral = (value) => {
21
+ const str = String(value);
22
+ const singleQuotes = (str.match(/'/g) || []).length;
23
+ const doubleQuotes = (str.match(/"/g) || []).length;
24
+
25
+ // JSON escaping, minus the enclosing double quotes.
26
+ const jsonBody = JSON.stringify(str).slice(1, -1);
27
+
28
+ if (singleQuotes > doubleQuotes) return `"${jsonBody}"`;
29
+
30
+ // Re-target the escaping at apostrophes: in a JSON body every `"` is escaped
31
+ // and every `'` is not, which is the mirror image of what a single-quoted
32
+ // literal needs.
33
+ const body = jsonBody.replace(/\\"/g, '"').replace(/'/g, "\\'");
34
+ return `'${body}'`;
35
+ };
36
+
37
+ module.exports = toJsStringLiteral;
@@ -10,6 +10,27 @@ concurrency:
10
10
  cancel-in-progress: true
11
11
 
12
12
  jobs:
13
+ # The `build` job below covers React end to end (gradle build, lint, unit + e2e) for
14
+ # the defaults app. This job keeps every framework's generator honest: for each one it
15
+ # generates the defaults app, the full-routes fixture app and an FDC3-channels app,
16
+ # and runs the ox lint gates over all three.
17
+ generated-apps:
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ framework: [react, webcomponents, angular]
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - name: Set up Node
27
+ uses: actions/setup-node@v4
28
+ with:
29
+ node-version: 22
30
+
31
+ - name: Generate and lint ${{ matrix.framework }} apps (default + full + fdc3)
32
+ run: ./.genx/scripts/generate-test-apps.sh ${{ matrix.framework }}
33
+
13
34
  build:
14
35
  env:
15
36
  genesisArtifactoryUser: ${{ secrets.JFROG_LIBS_RELEASE_CLIENT_RO_USER }}
@@ -40,7 +61,7 @@ jobs:
40
61
 
41
62
  - name: Generate sample app
42
63
  working-directory: /tmp
43
- run: npx -y @genesislcap/genx@latest init testapp -s "${{github.workspace}}" -x --enableDeployPlugin
64
+ run: npx -y @genesislcap/genx@latest init testapp -s "${{github.workspace}}" -x --framework react --enableDeployPlugin
44
65
 
45
66
  - name: Copy fixtures
46
67
  run: cp -R ./.genx/tests/fixtures/testapp/* /tmp/testapp
@@ -0,0 +1,96 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: [main, prerelease]
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ environment: deploy
12
+ concurrency:
13
+ group: publish-${{ github.ref }}
14
+ cancel-in-progress: false
15
+ permissions:
16
+ contents: read
17
+ id-token: write
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ with:
21
+ token: ${{secrets.GH_USER_TOKEN}}
22
+
23
+ - name: Set up Node
24
+ uses: actions/setup-node@v4
25
+ with:
26
+ node-version: 20
27
+ registry-url: https://registry.npmjs.org
28
+
29
+ - name: Upgrade npm for trusted publishing
30
+ run: npm install -g npm@^11.5.1
31
+
32
+ - name: Release
33
+ env:
34
+ GH_TOKEN: ${{secrets.GH_USER_TOKEN}}
35
+ run: |
36
+ touch /tmp/dist-tag # create a blank file, so it's always there even if we don't do a release
37
+ npm install
38
+ npm run release
39
+
40
+ - name: Set dist tag
41
+ id: dist-tag
42
+ run: |
43
+ NAME=$(node -p "require('./package.json').name")
44
+ VERSION=$(node -p "require('./package.json').version")
45
+
46
+ TAG=$(cat /tmp/dist-tag)
47
+ if [ -z "$TAG" ]; then
48
+ # No release happened this run. Derive the tag the current
49
+ # package.json version would have released under, so a
50
+ # previous run's failed publish can still be retried below.
51
+ #
52
+ # NOTE: this assumes the version's prerelease identifier and the
53
+ # semantic-release channel are the same string. That only holds
54
+ # because .releaserc's "prerelease" branch has no explicit
55
+ # "channel", so semantic-release defaults both the preid and the
56
+ # channel to the branch name. If that branch config ever sets an
57
+ # explicit "channel" or a differing "prerelease" identifier, this
58
+ # derivation will diverge from the real channel and this retry
59
+ # path will publish under the wrong dist-tag.
60
+ case "$VERSION" in
61
+ *-*) TAG="${VERSION#*-}"; TAG="${TAG%%.*}" ;;
62
+ *) TAG="latest" ;;
63
+ esac
64
+ fi
65
+
66
+ # Key the publish decision off the registry rather than off
67
+ # whether *this* run produced a release, so a flaky publish
68
+ # after semantic-release has already committed/tagged is
69
+ # retried on the next run instead of silently skipped.
70
+ # (stderr is captured separately so a stray npm notice on stderr
71
+ # can't masquerade as a real version and suppress the E404 check.)
72
+ if OUT=$(npm view "$NAME@$VERSION" version 2>/tmp/npm-view-err); then
73
+ [ -n "$OUT" ] && NEEDS_PUBLISH=false || NEEDS_PUBLISH=true
74
+ else
75
+ ERR=$(cat /tmp/npm-view-err)
76
+ case "$ERR" in
77
+ *E404*) NEEDS_PUBLISH=true ;;
78
+ *)
79
+ echo "::error::npm view failed unexpectedly: $ERR"
80
+ exit 1
81
+ ;;
82
+ esac
83
+ fi
84
+
85
+ echo "TAG=$TAG" >> "$GITHUB_OUTPUT"
86
+ echo "NEEDS_PUBLISH=$NEEDS_PUBLISH" >> "$GITHUB_OUTPUT"
87
+
88
+ - name: Generate .npmignore
89
+ run: cp .gitignore .npmignore && echo '!.gitignore' >> .npmignore && cat .npmignore
90
+
91
+ - name: NPM pack dry-run
92
+ run: npm pack --dry-run
93
+
94
+ - name: Publish
95
+ if: steps.dist-tag.outputs.NEEDS_PUBLISH == 'true'
96
+ run: npm publish --tag "${{ steps.dist-tag.outputs.TAG }}"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env sh
2
+ #
3
+ # Git pre-commit hook. Enabled by `npm run git:setup` from the client directory,
4
+ # which points core.hooksPath at this directory (husky).
5
+ #
6
+ # Runs lint-staged in client/, where the oxlint/oxfmt binaries and the
7
+ # lint-staged config live.
8
+ cd client && npx --no lint-staged
package/.releaserc CHANGED
@@ -59,7 +59,7 @@
59
59
  [
60
60
  "@semantic-release/exec",
61
61
  {
62
- "publishCmd": "echo \"${nextRelease.channel}\" > /tmp/dist-tag"
62
+ "publishCmd": "echo \"${nextRelease.channel || 'latest'}\" > /tmp/dist-tag"
63
63
  }
64
64
  ],
65
65
  [
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.23.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.22.1...v5.23.0) (2026-08-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * CI builds the react app; react jest stack repaired; store uses slice initial state [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 62db073
9
+ * oxlint/oxfmt as default linters [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 7ec4e78
10
+ * oxlint/oxfmt as default linters [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 6496554
11
+ * oxlint/oxfmt as default linters [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) (#575) b64f8b9
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * address code review - serializer robustness, permission semantics, tooling hardening [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 565c9ce
17
+ * address follow-up review - empty gridOptions, react CI coverage, husky invocation [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) c529d7e
18
+ * document-title fallback uses the app name, not 'React App' [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 5d099a7
19
+ * e2e tests actually navigate; template escaping and dead-code cleanups [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) b43ae68
20
+ * generated apps lint-clean out of the box for all frameworks, tiles and layouts [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) f8ee12f
21
+ * pre-commit lint fixes to convergence before gating [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) d9dc184
22
+ * quiet oxlint-staged fix passes and surface spawn failures [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 33ccf58
23
+ * schema import gating, FDC3 width pin, generated-dir glob alignment [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) dfbfd2a
24
+ * type-check and build cleanliness for generated apps [FUI-2489](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2489) 8838670
25
+
3
26
  ## [5.22.1](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.22.0...v5.22.1) (2026-08-06)
4
27
 
5
28
 
@@ -0,0 +1,32 @@
1
+ {
2
+ "semi": true,
3
+ "tabWidth": 2,
4
+ "singleQuote": true,
5
+ "bracketSpacing": true,
6
+ "printWidth": 100,
7
+ "htmlWhitespaceSensitivity": "ignore",
8
+ "sortPackageJson": false,
9
+ "ignorePatterns": [
10
+ "**/.git",
11
+ "**/.svn",
12
+ "**/.hg",
13
+ "**/node_modules",
14
+ "**/__tests__",
15
+ "**/__test__",
16
+ "**/.tmp",
17
+ "**/bootstrap",
18
+ "**/coverage",
19
+ "**/test-results",
20
+ "**/dist",
21
+ "**/.angular",
22
+ "**/out-tsc",
23
+ "**/temp",
24
+ "**/storybook-static",
25
+ "**/*.d.ts",
26
+ "**/*.md",
27
+ "**/*.hbs",
28
+ "**/.genx/**",
29
+ "**/src/generated/**",
30
+ "**/*.html"
31
+ ]
32
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "extends": ["./node_modules/@genesislcap/oxlint-config/.oxlintrc.json"]
4
+ }
@@ -25,7 +25,8 @@ Run `npm run test:e2e` to execute the end-to-end tests via a platform of your ch
25
25
 
26
26
  ## Linting
27
27
 
28
- Run `npm run lint` to lint the project.
28
+ Run `npm run lint` to lint the project (Oxlint → Oxfmt → Stylelint via `genx lint -l ox`).
29
+ Run `npm run lint:fix` to auto-fix lint findings and formatting.
29
30
 
30
31
  ## Styling
31
32
 
@@ -50,9 +51,8 @@ modes, and the selected mode is remembered across reloads.
50
51
  - `npm run dev:intellij`: Serves the project in development mode (specific for IntelliJ).
51
52
  - `npm run dev:https`: Serves the project in development mode with HTTPS.
52
53
  - `npm run dev:webpack`: Serves the project in development mode (specific for webpack configuration).
53
- - `npm run lint`: Lints the project using the specified profile.
54
- - `npm run lint:fix`: Fixes linting errors in the project.
55
- - `npm run lint:eslint`: Lints the project using ESLint and the specified profile.
54
+ - `npm run lint`: Lints the project (Oxlint Oxfmt → Stylelint).
55
+ - `npm run lint:fix`: Fixes lint findings and formatting.
56
56
  - `npm run lint:stylelint`: Lints CSS using a custom script.
57
57
  - `npm run test`: Runs unit tests.
58
58
  - `npm run test:e2e`: Runs end-to-end tests.
@@ -17,4 +17,4 @@ module.exports = {
17
17
  '\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/src/__mocks__/fileMock.js',
18
18
  '\\.(css)$': 'identity-obj-proxy',
19
19
  },
20
- };
20
+ };
@@ -1,7 +1,28 @@
1
1
  const { execSync } = require('child_process');
2
- const glob = require('glob');
2
+ const { readdirSync } = require('fs');
3
3
  const path = require('path');
4
- const cssFiles = glob.sync(path.join(__dirname, '**/*.css'));
4
+
5
+ const IGNORED_DIRS = new Set([
6
+ 'node_modules',
7
+ 'dist',
8
+ 'build',
9
+ 'coverage',
10
+ '.angular',
11
+ 'out-tsc',
12
+ 'test-results',
13
+ '.git',
14
+ ]);
15
+
16
+ function findCssFiles(dir) {
17
+ return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
18
+ if (entry.isDirectory()) {
19
+ return IGNORED_DIRS.has(entry.name) ? [] : findCssFiles(path.join(dir, entry.name));
20
+ }
21
+ return entry.isFile() && entry.name.endsWith('.css') ? [path.join(dir, entry.name)] : [];
22
+ });
23
+ }
24
+
25
+ const cssFiles = findCssFiles(__dirname);
5
26
 
6
27
  if (cssFiles.length === 0) {
7
28
  console.log('No CSS files found.');
@@ -15,4 +36,4 @@ try {
15
36
  } catch (error) {
16
37
  console.error('Error running stylelint:', error);
17
38
  process.exit(1);
18
- }
39
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Lints the staged files, fixing what can be fixed and failing on what cannot.
3
+ *
4
+ * `oxlint --fix` is not single-pass convergent: it applies at most one fix per
5
+ * overlapping span, so a file with several unused specifiers in one import
6
+ * statement still reports the rest afterwards. A single
7
+ * `oxlint --fix --deny-warnings` would therefore reject a commit whose findings
8
+ * are entirely auto-fixable, and lint-staged would revert the partial fix. There
9
+ * is no `--fix-passes` flag (oxlint 1.78), so the loop lives here; it can go
10
+ * back to one plain call once oxlint converges on its own.
11
+ *
12
+ * The fixing passes stay quiet so a remaining finding is reported once, by the
13
+ * final pass, rather than once per attempt.
14
+ */
15
+ const { spawnSync } = require('node:child_process');
16
+
17
+ /** Convergence needs about log2(findings) passes; this is well clear of that. */
18
+ const MAX_PASSES = 10;
19
+
20
+ const files = process.argv.slice(2);
21
+
22
+ const runOxlint = (args, quiet) =>
23
+ spawnSync('oxlint', [...args, ...files], {
24
+ stdio: quiet ? 'ignore' : 'inherit',
25
+ shell: process.platform === 'win32',
26
+ });
27
+
28
+ if (files.length === 0) {
29
+ process.exit(0);
30
+ }
31
+
32
+ for (let pass = 0; pass < MAX_PASSES; pass += 1) {
33
+ const { status, error } = runOxlint(['--fix', '--deny-warnings'], true);
34
+ if (error) {
35
+ // Never fail silently: a missing binary must not look like a lint failure.
36
+ console.error(`oxlint-staged: could not run oxlint (${error.code})`);
37
+ process.exit(1);
38
+ }
39
+ if (status === 0) {
40
+ process.exit(0);
41
+ }
42
+ }
43
+
44
+ // Still not clean: report what is left (without touching the files again) and fail.
45
+ process.exit(runOxlint(['--deny-warnings'], false).status === 0 ? 0 : 1);
@@ -28,9 +28,9 @@
28
28
  "dev:intellij": "npm run dev",
29
29
  "dev:https": "npm run dev -- --https",
30
30
  "dev:webpack": "npm run dev",
31
- "lint": "genx lint --profile",
32
- "lint:fix": "genx lint --fix",
33
- "lint:eslint": "genx lint -l eslint --profile",
31
+ "git:setup": "cd .. && npx --no --prefix client husky install",
32
+ "lint": "genx lint -l ox",
33
+ "lint:fix": "genx lint -l ox --fix",
34
34
  "lint:stylelint": "node lint-css.js",
35
35
  "test": "jest",
36
36
  "test:e2e": "genx test --e2e",
@@ -39,39 +39,49 @@
39
39
  "test:coverage": "ng test --no-watch --code-coverage",
40
40
  "test:unit:watch": "ng test --watch"
41
41
  },
42
- "eslintConfig": {
43
- "extends": "@genesislcap/eslint-config",
44
- "ignorePatterns": ["**/dist/**", "**/public/**", "**/coverage/**", "**/test/**", "playwright.config.ts"]
42
+ "lint-staged": {
43
+ "*.{ts,js,tsx,jsx}": [
44
+ "node oxlint-staged.js",
45
+ "oxfmt --write"
46
+ ],
47
+ "*.{mjs,cjs}": [
48
+ "oxfmt --write"
49
+ ]
45
50
  },
46
51
  "stylelint": {
47
52
  "extends": "@genesislcap/stylelint-config"
48
53
  },
49
- "prettier": "@genesislcap/prettier-config",
50
54
  "devDependencies": {
51
55
  "@angular-builders/custom-webpack": "^18.0.0",
52
56
  "@angular-devkit/build-angular": "^18.0.4",
53
57
  "@angular/cli": "^18.0.4",
54
58
  "@angular/compiler-cli": "^18.0.4",
55
59
  "@genesislcap/design-system-configurator": "{{versions.UI}}",
56
- "@genesislcap/eslint-config": "{{versions.UI}}",
57
- "@genesislcap/prettier-config": "{{versions.UI}}",
58
60
  "@genesislcap/foundation-testing": "{{versions.UI}}",
59
61
  "@genesislcap/genx": "{{versions.UI}}",
60
62
  "@genesislcap/event-type-codegen": "{{versions.UI}}",
61
63
  "@genesislcap/build-kit": "{{versions.UI}}",
64
+ "@genesislcap/oxlint-config": "{{versions.UI}}",
65
+ "@genesislcap/stylelint-config": "{{versions.UI}}",
62
66
  "@types/jasmine": "~5.1.0",
63
67
  "@types/jest": "^29.5.13",
64
68
  "@types/numeral": "^2.0.5",
65
69
  "file-loader": "^6.2.0",
70
+ "husky": "^7.0.4",
66
71
  "jasmine-core": "~5.1.0",
67
72
  "jest": "^29.7.0",
68
73
  "jest-environment-jsdom": "^29.7.0",
69
74
  "jest-preset-angular": "^14.2.4",
70
75
  "jest-transform-stub": "^2.0.0",
76
+ "lint-staged": "^12.4.1",
77
+ "oxfmt": "^0.44.0",
78
+ "oxlint": "^1.77.0",
71
79
  "svg-url-loader": "^8.0.0",
80
+ "ts-jest": "^29.2.5",
72
81
  "typescript": "~5.4.5",
73
82
  "ts-node": "10.9.2",
74
83
  "dotenv": "16.4.5",
84
+ "webpack": "5.94.0",
75
85
  "@types/node": "20.14.11"
76
86
  },
77
87
  "dependencies": {
@@ -1,21 +1,22 @@
1
1
  import { configDefaults } from '@genesislcap/foundation-testing/e2e';
2
- import { environment } from './src/environments/environment';
3
2
  import * as dotenv from 'dotenv';
3
+ import { environment } from './src/environments/environment';
4
4
 
5
5
  dotenv.config();
6
6
 
7
7
  const PROTOCOL = process.env['PROTOCOL'] || environment.PROTOCOL || 'http';
8
8
  const HOST = process.env['HOST'] || environment.HOST || 'localhost';
9
+ // oxlint-disable-next-line no-magic-numbers -- 4200 is the standard Angular dev server port
9
10
  const PORT = process.env['PORT'] || environment.PORT || 4200;
10
11
 
11
12
  export default {
12
- ...configDefaults,
13
- webServer: {
14
- ...configDefaults.webServer,
15
- url: `${PROTOCOL}://${HOST}:${PORT}`,
16
- },
17
- use: {
18
- ...configDefaults.use,
19
- baseURL: `${PROTOCOL}://${HOST}:${PORT}`,
20
- }
21
- };
13
+ ...configDefaults,
14
+ webServer: {
15
+ ...configDefaults.webServer,
16
+ url: `${PROTOCOL}://${HOST}:${PORT}`,
17
+ },
18
+ use: {
19
+ ...configDefaults.use,
20
+ baseURL: `${PROTOCOL}://${HOST}:${PORT}`,
21
+ },
22
+ };
@@ -1,2 +1 @@
1
1
  <ng-container *appLayoutLazyLoad="layoutName"></ng-container>
2
-
@@ -1,15 +1,15 @@
1
1
  import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit } from '@angular/core';
2
2
  import { Router, NavigationEnd } from '@angular/router';
3
3
  import { Connect } from '@genesislcap/foundation-comms';
4
- import getLayoutNameByRoute from './utils/getLayoutNameByRoute';
5
- import type { LayoutComponentName } from './types/layout';
4
+ import { customEventFactory, registerStylesTarget } from '../pbc/utils';
6
5
  import { configureFoundationAuth } from './share/foundation-auth';
7
6
  import { registerComponents } from './share/genesis-components';
8
7
  import { getStore } from './store/foundation-store';
9
- import { customEventFactory, registerStylesTarget } from '../pbc/utils';
8
+ import type { LayoutComponentName } from './types/layout';
10
9
  {{#if FDC3.channels.length}}
11
10
  import { listenToChannel, onFDC3Ready } from './utils';
12
11
  {{/if}}
12
+ import getLayoutNameByRoute from './utils/getLayoutNameByRoute';
13
13
 
14
14
  @Component({
15
15
  selector: '{{rootElement}}',
@@ -30,7 +30,7 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
30
30
  ) {
31
31
  // @ts-ignore
32
32
  configureFoundationAuth({ router, connectService: this.connect });
33
-
33
+
34
34
  // Set layout componet based on route
35
35
  router.events.subscribe((event: any) => {
36
36
  if (event instanceof NavigationEnd) {
@@ -76,22 +76,21 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
76
76
  this.el.nativeElement.dispatchEvent(customEventFactory(type, detail));
77
77
  }
78
78
 
79
-
79
+ {{#if FDC3.channels.length}}
80
80
  ngAfterViewInit() {
81
- {{#if FDC3.channels.length}}
82
81
  onFDC3Ready(this.FDC3ReadyHandler);
83
- {{/if}}
84
82
  }
85
83
 
86
- {{#if FDC3.channels.length}}
87
84
  FDC3ReadyHandler = () => {
88
85
  {{#each FDC3.channels}}
89
86
  listenToChannel('{{this.name}}', '{{this.type}}', (result) => {
90
- console.log('Received FDC3 channel message on: {{this.name}} channel, type: {{this.type}}', result);
87
+ console.log('Received FDC3 message on {{this.name}} ({{this.type}})', result);
91
88
  // TODO: Add your listener logic here
92
89
  // E.g. open a modal or route to specific page: Route.path.push(`[Route name]`);
93
90
  });
94
91
  {{/each}}
95
92
  };
93
+ {{else}}
94
+ ngAfterViewInit() {}
96
95
  {{/if}}
97
96
  }
@@ -1,7 +1,7 @@
1
1
  import type { LayoutComponentName } from './types/layout';
2
2
 
3
- export const AUTH_PATH = 'login'
4
- export const NOT_PERMITTED_PATH = 'not-permitted'
3
+ export const AUTH_PATH = 'login';
4
+ export const NOT_PERMITTED_PATH = 'not-permitted';
5
5
 
6
6
  export const layoutComponentName = {
7
7
  default: 'DefaultLayoutComponent',
@@ -1,15 +1,15 @@
1
1
  import { NgModule, CUSTOM_ELEMENTS_SCHEMA, APP_INITIALIZER } from '@angular/core';
2
- import { BrowserModule } from '@angular/platform-browser';
3
2
  import { FormsModule } from '@angular/forms';
3
+ import { BrowserModule } from '@angular/platform-browser';
4
4
  import { Router, RouterModule } from '@angular/router';
5
+ import { provideRedux } from '@reduxjs/angular-redux';
5
6
  import { PBCContainer } from '../pbc/container';
6
7
  import { PBCElementsRenderer } from '../pbc/elementsRenderer';
7
8
  import { AppComponent } from './app.component';
8
- import { DefaultLayoutComponent } from './layouts/default/default.layout';
9
- import { BlankLayoutComponent } from './layouts/blank/blank.layout';
10
9
  import { LayoutLazyLoadDirective } from './directive/app-lazy-load.directive';
10
+ import { BlankLayoutComponent } from './layouts/blank/blank.layout';
11
+ import { DefaultLayoutComponent } from './layouts/default/default.layout';
11
12
  import { RouteService } from './services/route.service';
12
- import { provideRedux } from '@reduxjs/angular-redux';
13
13
  import { reduxStore } from './store/store';
14
14
 
15
15
  @NgModule({
@@ -31,7 +31,8 @@ import { reduxStore } from './store/store';
31
31
  provideRedux({ store: reduxStore }),
32
32
  {
33
33
  provide: APP_INITIALIZER,
34
- useFactory: (service: RouteService, router: Router) => () => router.resetConfig(service.allRoutes()),
34
+ useFactory: (service: RouteService, router: Router) => () =>
35
+ router.resetConfig(service.allRoutes()),
35
36
  deps: [RouteService, Router],
36
37
  multi: true,
37
38
  },
@@ -1,15 +1,29 @@
1
- <section class="error-message-wrapper">
2
- <div class="error-message" style="color: var(--neutral-foreground-rest); background-color: var(--neutral-layer-4); border-color: var(--error-color); border-radius: 7px; border-style: solid; border-width: 4px; padding: 5px; margin: 15px; text-align: center; width: fit-content;">
3
- <ng-container [ngSwitch]="elementType">
4
- <h1 *ngSwitchCase="'h1'">\{{ message }}</h1>
5
- <h2 *ngSwitchCase="'h2'">\{{ message }}</h2>
6
- <h3 *ngSwitchCase="'h3'">\{{ message }}</h3>
7
- <h4 *ngSwitchCase="'h4'">\{{ message }}</h4>
8
- <h5 *ngSwitchCase="'h5'">\{{ message }}</h5>
9
- <h6 *ngSwitchCase="'h6'">\{{ message }}</h6>
10
- <p *ngSwitchCase="'p'">\{{ message }}</p>
11
- <span *ngSwitchCase="'span'">\{{ message }}</span>
12
- <div *ngSwitchDefault>\{{ message }}</div>
13
- </ng-container>
14
- </div>
15
- </section>
1
+ <section class="error-message-wrapper">
2
+ <div
3
+ class="error-message"
4
+ style="
5
+ color: var(--neutral-foreground-rest);
6
+ background-color: var(--neutral-layer-4);
7
+ border-color: var(--error-color);
8
+ border-radius: 7px;
9
+ border-style: solid;
10
+ border-width: 4px;
11
+ padding: 5px;
12
+ margin: 15px;
13
+ text-align: center;
14
+ width: fit-content;
15
+ "
16
+ >
17
+ <ng-container [ngSwitch]="elementType">
18
+ <h1 *ngSwitchCase="'h1'">\{{ message }}</h1>
19
+ <h2 *ngSwitchCase="'h2'">\{{ message }}</h2>
20
+ <h3 *ngSwitchCase="'h3'">\{{ message }}</h3>
21
+ <h4 *ngSwitchCase="'h4'">\{{ message }}</h4>
22
+ <h5 *ngSwitchCase="'h5'">\{{ message }}</h5>
23
+ <h6 *ngSwitchCase="'h6'">\{{ message }}</h6>
24
+ <p *ngSwitchCase="'p'">\{{ message }}</p>
25
+ <span *ngSwitchCase="'span'">\{{ message }}</span>
26
+ <div *ngSwitchDefault>\{{ message }}</div>
27
+ </ng-container>
28
+ </div>
29
+ </section>
@@ -66,9 +66,9 @@ describe('ErrorMessageComponent', () => {
66
66
  component.elementType = 'h2';
67
67
  component.message = 'Testing class application.';
68
68
  fixture.detectChanges();
69
-
69
+
70
70
  const compiled = fixture.nativeElement;
71
71
  const element = compiled.querySelector('.error-message');
72
72
  expect(element).toBeTruthy();
73
73
  });
74
- });
74
+ });
@@ -1,15 +1,13 @@
1
- import { Component, Input } from '@angular/core';
2
1
  import { CommonModule } from '@angular/common';
2
+ import { Component, Input } from '@angular/core';
3
3
 
4
4
  @Component({
5
5
  selector: 'app-error-message',
6
6
  templateUrl: './error-message.component.html',
7
7
  standalone: true,
8
- imports: [
9
- CommonModule,
10
- ],
8
+ imports: [CommonModule],
11
9
  })
12
10
  export class ErrorMessageComponent {
13
11
  @Input() elementType: string = 'div';
14
12
  @Input() message: string = '';
15
- }
13
+ }