@abgov/nx-adsp 13.7.1 → 13.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-adsp",
3
- "version": "13.7.1",
3
+ "version": "13.7.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for ADSP apps.",
@@ -18,6 +18,7 @@
18
18
  "@nx/express": "^23.0.0",
19
19
  "@nx/react": "^23.0.0",
20
20
  "@nx/vue": "^23.0.0",
21
+ "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0",
21
22
  "tslib": "^2.0.0"
22
23
  },
23
24
  "peerDependenciesMeta": {
@@ -157,7 +157,7 @@ import { authorize, createValidationHandler } from '@abgov/adsp-service-sdk';
157
157
  import { z } from 'zod';
158
158
  import * as items from '../services/items';
159
159
 
160
- const CreateItem = z.object({ name: z.string().min(1) });
160
+ const CreateItem = z.object({ name: z.string().trim().min(1) });
161
161
 
162
162
  export function itemsRouter(): Router {
163
163
  const router = Router();
@@ -178,7 +178,13 @@ export function itemsRouter(): Router {
178
178
  createValidationHandler(CreateItem),
179
179
  async (req, res, next) => {
180
180
  try {
181
- const { name } = req.body as z.infer<typeof CreateItem>;
181
+ // createValidationHandler validates req.body against the schema but
182
+ // doesn't replace it with the parsed result — req.body is still the
183
+ // raw body afterward. Invisible for a plain string, but any
184
+ // transform (.trim() here) or coercion (z.coerce.date(), a default)
185
+ // is silently lost if you read req.body directly instead of
186
+ // re-parsing. Parse again, here, to get the actual parsed value.
187
+ const { name } = CreateItem.parse(req.body);
182
188
  res.status(201).json(await items.create(name));
183
189
  } catch (err) {
184
190
  next(err);
@@ -201,7 +207,10 @@ app.use('/<%= projectName %>/v1/items', itemsRouter());
201
207
 
202
208
  `authorize(role)` → 403 if the user lacks the role; `createValidationHandler(schema)`
203
209
  → 400 on a bad body. Both forward errors to `next()`, which `createErrorHandler`
204
- (mounted last in `main.ts`) turns into structured HTTP responses.
210
+ (mounted last in `main.ts`) turns into structured HTTP responses. Don't read
211
+ `req.body` directly in the handler and assert its type (`req.body as
212
+ z.infer<typeof Schema>`) — that's the raw, unparsed body, not the validated
213
+ one; parse it again with the same schema instead, as above.
205
214
 
206
215
  ## Recipe: add a resource end to end
207
216
 
@@ -46,7 +46,11 @@ export function exampleRouter(eventService: EventService): Router {
46
46
  createValidationHandler(ExampleRequestSchema),
47
47
  async (req, res, next) => {
48
48
  try {
49
- const { id } = req.body as z.infer<typeof ExampleRequestSchema>;
49
+ // createValidationHandler validates req.body but doesn't replace it
50
+ // with the parsed result — parse again here rather than asserting
51
+ // req.body's type, so any transform/coercion your schema adds later
52
+ // actually takes effect (see AGENTS.md's "Adding routes").
53
+ const { id } = ExampleRequestSchema.parse(req.body);
50
54
  eventService.send(createExampleEvent(id));
51
55
  res.json({ id });
52
56
  } catch (err) {
@@ -16,11 +16,26 @@ vi.mock('@dsb-norge/vue-keycloak-js', async () => {
16
16
  });
17
17
 
18
18
  import { useKeycloak } from '@dsb-norge/vue-keycloak-js';
19
+ import { createMemoryHistory, createRouter } from 'vue-router';
19
20
  import App from './App.vue';
20
21
 
22
+ // App.vue reads useRoute() for the layout-width feature (route.meta.layout,
23
+ // default 'page') — that needs a real router installed via `global.plugins`,
24
+ // not just a RouterView stub. Stubbing RouterView only replaces what it
25
+ // renders; it doesn't provide the injection context useRoute() reads from, so
26
+ // route.meta would throw on undefined without this. Installing the router is
27
+ // enough on its own — its route ref is already a real object (Vue Router's
28
+ // START_LOCATION_NORMALIZED, meta: {}) before any navigation resolves, so no
29
+ // router.isReady()/await is needed, and calling it here would hang: with no
30
+ // app mounted yet, no navigation has been triggered for it to wait on.
31
+ const router = createRouter({
32
+ history: createMemoryHistory(),
33
+ routes: [{ path: '/', component: { template: '<div />' } }],
34
+ });
35
+
21
36
  describe('App shell header', () => {
22
37
  it('shows Sign in when unauthenticated', () => {
23
- const wrapper = mount(App, { global: { stubs: { RouterView: true } } });
38
+ const wrapper = mount(App, { global: { plugins: [router], stubs: { RouterView: true } } });
24
39
  const group = wrapper.find('goa-button-group');
25
40
  expect(group.exists()).toBe(true);
26
41
  expect(group.html()).toContain('Sign in');
@@ -30,7 +45,7 @@ describe('App shell header', () => {
30
45
  // The bug: destructuring useKeycloak() froze `keycloak` at undefined, so
31
46
  // login() was a permanent no-op. Reactive access must reach the instance.
32
47
  const kc = useKeycloak();
33
- const wrapper = mount(App, { global: { stubs: { RouterView: true } } });
48
+ const wrapper = mount(App, { global: { plugins: [router], stubs: { RouterView: true } } });
34
49
  const btn = wrapper.findAll('goa-button').find((b) => b.text().includes('Sign in'));
35
50
  await btn?.trigger('_click');
36
51
  expect(kc.keycloak?.login).toHaveBeenCalled();
@@ -6,6 +6,7 @@ exports.vueComponentsImportPath = vueComponentsImportPath;
6
6
  exports.default = default_1;
7
7
  const tslib_1 = require("tslib");
8
8
  const devkit_1 = require("@nx/devkit");
9
+ const internal_1 = require("@nx/eslint/internal");
9
10
  const path = require("path");
10
11
  // Backfills package.json module-resolution fields for the library. In a TS-solution
11
12
  // workspace @nx/vue's library resolves via package.json `exports`, but its
@@ -36,24 +37,32 @@ function ensurePackageExports(host, libRoot) {
36
37
  // element usage, not the deprecated Vue 2 component-slot syntax the rule targets.
37
38
  // The whole lib wraps web components, so it's scoped to the lib (consuming apps,
38
39
  // which use `<template #actions>`, are unaffected).
40
+ //
41
+ // Uses @nx/eslint's own override helpers rather than hand-editing a specific
42
+ // file format: create-nx-workspace's current default is flat config
43
+ // (eslint.config.mjs), which is a JS module exporting an array, not JSON — an
44
+ // earlier version of this function only handled legacy .eslintrc.json, so it
45
+ // silently no-op'd (just a console warning) on flat-config workspaces, and
46
+ // `nx lint vue-components` failed for real on unmodified generator output.
47
+ // addOverrideToLintConfig/lintConfigHasOverride detect flat vs legacy
48
+ // internally (via @nx/eslint's own useFlatConfig check) and edit whichever is
49
+ // actually in effect — AST-aware for flat config, not string splicing.
39
50
  function disableSlotAttributeRule(host, libRoot) {
40
- const eslintrc = `${libRoot}/.eslintrc.json`;
41
- if (!host.exists(eslintrc)) {
42
- console.warn(`\n⚠ ${eslintrc} not found — could not disable vue/no-deprecated-slot-attribute.\n` +
51
+ if (!(0, internal_1.isEslintConfigSupported)(host, libRoot)) {
52
+ console.warn(`\n⚠ No ESLint config found for ${libRoot} — could not disable vue/no-deprecated-slot-attribute.\n` +
43
53
  ` GoabModal uses goa-modal's native \`slot\` attribute; if lint flags it, turn\n` +
44
54
  ` that rule off for this library.\n`);
45
55
  return;
46
56
  }
47
- (0, devkit_1.updateJson)(host, eslintrc, (json) => {
48
- var _a, _b;
49
- const overrides = ((_a = json.overrides) !== null && _a !== void 0 ? _a : (json.overrides = []));
50
- let vueOverride = overrides.find((o) => { var _a; return (_a = o.files) === null || _a === void 0 ? void 0 : _a.some((f) => f.includes('vue')); });
51
- if (!vueOverride) {
52
- vueOverride = { files: ['*.vue'], rules: {} };
53
- overrides.push(vueOverride);
54
- }
55
- ((_b = vueOverride.rules) !== null && _b !== void 0 ? _b : (vueOverride.rules = {}))['vue/no-deprecated-slot-attribute'] = 'off';
56
- return json;
57
+ // `files` is `string | string[]` per the Linter type — normalize before checking.
58
+ const isVueOverride = (o) => (Array.isArray(o.files) ? o.files : o.files ? [o.files] : []).some((f) => f.includes('vue'));
59
+ const alreadyDisabled = (0, internal_1.lintConfigHasOverride)(host, libRoot, (o) => { var _a; return isVueOverride(o) && ((_a = o.rules) === null || _a === void 0 ? void 0 : _a['vue/no-deprecated-slot-attribute']) === 'off'; });
60
+ if (alreadyDisabled) {
61
+ return;
62
+ }
63
+ (0, internal_1.addOverrideToLintConfig)(host, libRoot, {
64
+ files: ['*.vue'],
65
+ rules: { 'vue/no-deprecated-slot-attribute': 'off' },
57
66
  });
58
67
  }
59
68
  exports.LIB_NAME = 'vue-components';
@@ -1 +1 @@
1
- {"version":3,"file":"vue-components.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/vue-components/vue-components.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vue-components.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/vue-components/vue-components.ts"],"names":[],"mappings":"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"}
@@ -50,12 +50,34 @@ describe('Vue Components Generator', () => {
50
50
  expect(agents).toContain('defineModel<boolean>');
51
51
  }, 30000);
52
52
 
53
+ it('disables vue/no-deprecated-slot-attribute in flat config too, not just .eslintrc.json', async () => {
54
+ // useFlatConfig() (from @nx/eslint) treats a root flat-config file's
55
+ // presence as authoritative, regardless of the installed ESLint version —
56
+ // this is what create-nx-workspace's current default actually looks like.
57
+ host.write('eslint.config.mjs', 'export default [];\n');
58
+ await generator(host);
59
+
60
+ expect(host.exists('libs/vue-components/eslint.config.mjs')).toBeTruthy();
61
+ expect(host.exists('libs/vue-components/.eslintrc.json')).toBeFalsy();
62
+ const flatConfig = host.read('libs/vue-components/eslint.config.mjs').toString();
63
+ expect(flatConfig).toContain('"vue/no-deprecated-slot-attribute": "off"');
64
+ }, 30000);
65
+
53
66
  it('is idempotent — a second run does not throw and keeps the wrappers', async () => {
54
67
  await generator(host);
55
68
  await expect(generator(host)).resolves.not.toThrow();
56
69
  expect(host.exists('libs/vue-components/src/lib/GoabInput.vue')).toBeTruthy();
57
70
  }, 30000);
58
71
 
72
+ it('does not duplicate the ESLint override on a second run (legacy or flat)', async () => {
73
+ host.write('eslint.config.mjs', 'export default [];\n');
74
+ await generator(host);
75
+ await generator(host);
76
+
77
+ const flatConfig = host.read('libs/vue-components/eslint.config.mjs').toString();
78
+ expect(flatConfig.split('vue/no-deprecated-slot-attribute').length - 1).toBe(1);
79
+ }, 30000);
80
+
59
81
  it('derives the import path from the workspace scope', () => {
60
82
  expect(vueComponentsImportPath(host)).toMatch(/\/vue-components$/);
61
83
  });