@nordcraft/search 1.0.88 → 1.0.90

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 (52) hide show
  1. package/dist/rules/issues/actions/noReferenceProjectActionRule.js +2 -27
  2. package/dist/rules/issues/actions/noReferenceProjectActionRule.js.map +1 -1
  3. package/dist/rules/issues/actions/projectActionIsReferenced.memo.d.ts +3 -0
  4. package/dist/rules/issues/actions/projectActionIsReferenced.memo.js +38 -0
  5. package/dist/rules/issues/actions/projectActionIsReferenced.memo.js.map +1 -0
  6. package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js +2 -2
  7. package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js.map +1 -1
  8. package/dist/rules/issues/components/componentIsReferenced.memo.d.ts +3 -0
  9. package/dist/rules/issues/components/componentIsReferenced.memo.js +25 -0
  10. package/dist/rules/issues/components/componentIsReferenced.memo.js.map +1 -0
  11. package/dist/rules/issues/components/noReferenceComponentRule.d.ts +1 -3
  12. package/dist/rules/issues/components/noReferenceComponentRule.js +8 -26
  13. package/dist/rules/issues/components/noReferenceComponentRule.js.map +1 -1
  14. package/dist/rules/issues/dom/nonEmptyVoidElementRule.js +1 -1
  15. package/dist/rules/issues/dom/nonEmptyVoidElementRule.js.map +1 -1
  16. package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js +2 -85
  17. package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js.map +1 -1
  18. package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.d.ts +3 -0
  19. package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.js +84 -0
  20. package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.js.map +1 -0
  21. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js +1 -1
  22. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js.map +1 -1
  23. package/dist/rules/issues/miscellaneous/miscRules.index.js +2 -0
  24. package/dist/rules/issues/miscellaneous/miscRules.index.js.map +1 -1
  25. package/dist/rules/issues/miscellaneous/noReferencePackageRule.d.ts +5 -0
  26. package/dist/rules/issues/miscellaneous/noReferencePackageRule.js +54 -0
  27. package/dist/rules/issues/miscellaneous/noReferencePackageRule.js.map +1 -0
  28. package/dist/rules/issues/style/styleRules.index.d.ts +3 -1
  29. package/dist/rules/issues/style/unknownCSSVariable.d.ts +3 -2
  30. package/dist/rules/issues/style/unknownCSSVariable.js +79 -36
  31. package/dist/rules/issues/style/unknownCSSVariable.js.map +1 -1
  32. package/dist/searchProject.js +23 -0
  33. package/dist/searchProject.js.map +1 -1
  34. package/dist/types.d.ts +11 -4
  35. package/package.json +3 -3
  36. package/src/rules/issues/actions/noReferenceProjectActionRule.ts +2 -32
  37. package/src/rules/issues/actions/projectActionIsReferenced.memo.ts +52 -0
  38. package/src/rules/issues/attributes/noReferenceAttributeInInstanceRule.ts +2 -2
  39. package/src/rules/issues/components/componentIsReferenced.memo.ts +39 -0
  40. package/src/rules/issues/components/noReferenceComponentRule.ts +10 -39
  41. package/src/rules/issues/dom/nonEmptyVoidElementRule.ts +1 -1
  42. package/src/rules/issues/formulas/noReferenceProjectFormulaRule.test.ts +33 -0
  43. package/src/rules/issues/formulas/noReferenceProjectFormulaRule.ts +2 -102
  44. package/src/rules/issues/formulas/projectFormulaIsReferenced.memo.ts +112 -0
  45. package/src/rules/issues/miscellaneous/createStaticSizeConstraintRule.ts +1 -1
  46. package/src/rules/issues/miscellaneous/miscRules.index.ts +2 -0
  47. package/src/rules/issues/miscellaneous/noReferencePackageRule.test.ts +289 -0
  48. package/src/rules/issues/miscellaneous/noReferencePackageRule.ts +72 -0
  49. package/src/rules/issues/style/unknownCSSVariable.test.ts +107 -44
  50. package/src/rules/issues/style/unknownCSSVariable.ts +101 -46
  51. package/src/searchProject.ts +24 -0
  52. package/src/types.ts +13 -0
@@ -1,4 +1,6 @@
1
+ import type { ProjectFiles } from '@nordcraft/ssr/dist/src/ssr.types'
1
2
  import { describe, expect, test } from 'bun:test'
3
+ import { fixProject } from '../../../fixProject'
2
4
  import { searchProject } from '../../../searchProject'
3
5
  import { unknownCSSVariableRule } from './unknownCSSVariable'
4
6
 
@@ -171,7 +173,7 @@ describe('unknownCSSVariableRule', () => {
171
173
  expect(problems[1].details).toEqual({ name: '--undefined-local' })
172
174
  })
173
175
 
174
- test('should not report when legacy style variables are used', () => {
176
+ test('should report problem if local variable is used in parent but only defined in child', () => {
175
177
  const problems = Array.from(
176
178
  searchProject({
177
179
  files: {
@@ -190,19 +192,26 @@ describe('unknownCSSVariableRule', () => {
190
192
  tag: 'div',
191
193
  type: 'element',
192
194
  attrs: {},
193
- 'style-variables': [
194
- {
195
- name: 'legacy-variable',
195
+ style: {
196
+ color: 'var(--child-only-variable)',
197
+ },
198
+ events: {},
199
+ classes: {},
200
+ children: ['child'],
201
+ },
202
+ child: {
203
+ tag: 'div',
204
+ type: 'element',
205
+ attrs: {},
206
+ customProperties: {
207
+ '--child-only-variable': {
196
208
  formula: {
197
209
  type: 'value',
198
- value: '10px',
210
+ value: 'red',
199
211
  },
200
- category: 'spacing',
201
212
  },
202
- ],
203
- style: {
204
- margin: 'var(--legacy-variable)',
205
213
  },
214
+ style: {},
206
215
  events: {},
207
216
  classes: {},
208
217
  children: [],
@@ -219,10 +228,10 @@ describe('unknownCSSVariableRule', () => {
219
228
  }),
220
229
  )
221
230
 
222
- expect(problems).toBeEmpty()
231
+ expect(problems).toHaveLength(1)
223
232
  })
224
233
 
225
- test('should not report when declared in a style', () => {
234
+ test('should not report when legacy style variables are used', () => {
226
235
  const problems = Array.from(
227
236
  searchProject({
228
237
  files: {
@@ -241,9 +250,18 @@ describe('unknownCSSVariableRule', () => {
241
250
  tag: 'div',
242
251
  type: 'element',
243
252
  attrs: {},
253
+ 'style-variables': [
254
+ {
255
+ name: 'legacy-variable',
256
+ formula: {
257
+ type: 'value',
258
+ value: '10px',
259
+ },
260
+ category: 'spacing',
261
+ },
262
+ ],
244
263
  style: {
245
- '--declared-in-style': '10px',
246
- margin: 'var(--declared-in-style)',
264
+ margin: 'var(--legacy-variable)',
247
265
  },
248
266
  events: {},
249
267
  classes: {},
@@ -264,13 +282,14 @@ describe('unknownCSSVariableRule', () => {
264
282
  expect(problems).toBeEmpty()
265
283
  })
266
284
 
267
- test('should not report when a legacy theme exists (no propertyDefinitions)', () => {
285
+ test('should not report when declared in a style', () => {
268
286
  const problems = Array.from(
269
287
  searchProject({
270
288
  files: {
271
289
  themes: {
272
290
  Default: {
273
291
  fonts: [],
292
+ propertyDefinitions: {},
274
293
  },
275
294
  },
276
295
  formulas: {},
@@ -283,7 +302,8 @@ describe('unknownCSSVariableRule', () => {
283
302
  type: 'element',
284
303
  attrs: {},
285
304
  style: {
286
- color: 'var(--legacy-theme-variable)',
305
+ '--declared-in-style': '10px',
306
+ margin: 'var(--declared-in-style)',
287
307
  },
288
308
  events: {},
289
309
  classes: {},
@@ -304,32 +324,26 @@ describe('unknownCSSVariableRule', () => {
304
324
  expect(problems).toBeEmpty()
305
325
  })
306
326
 
307
- test('should not report if CSS variable is defined in any other component or self', () => {
327
+ test('should not report when a legacy theme exists (no propertyDefinitions)', () => {
308
328
  const problems = Array.from(
309
329
  searchProject({
310
330
  files: {
311
331
  themes: {
312
332
  Default: {
313
333
  fonts: [],
314
- propertyDefinitions: {},
315
334
  },
316
335
  },
317
336
  formulas: {},
318
337
  components: {
319
- componentA: {
320
- name: 'componentA',
338
+ test: {
339
+ name: 'test',
321
340
  nodes: {
322
341
  root: {
323
342
  tag: 'div',
324
343
  type: 'element',
325
- customProperties: {
326
- '--variable-in-a': {
327
- formula: { type: 'value', value: 'red' },
328
- },
329
- },
330
344
  attrs: {},
331
345
  style: {
332
- color: 'var(--variable-in-a)',
346
+ color: 'var(--legacy-theme-variable)',
333
347
  },
334
348
  events: {},
335
349
  classes: {},
@@ -341,32 +355,81 @@ describe('unknownCSSVariableRule', () => {
341
355
  attributes: {},
342
356
  variables: {},
343
357
  },
344
- componentB: {
345
- name: 'componentB',
346
- nodes: {
347
- root: {
348
- tag: 'div',
349
- type: 'element',
350
- attrs: {},
351
- style: {
352
- color: 'var(--variable-in-a)',
353
- },
354
- events: {},
355
- classes: {},
356
- children: [],
357
- },
358
+ },
359
+ },
360
+ rules: [unknownCSSVariableRule],
361
+ }),
362
+ )
363
+
364
+ expect(problems).toBeEmpty()
365
+ })
366
+
367
+ test('fix should add variable to theme', () => {
368
+ const files: ProjectFiles = {
369
+ themes: {
370
+ Default: {
371
+ fonts: [],
372
+ propertyDefinitions: {},
373
+ },
374
+ },
375
+ formulas: {},
376
+ components: {
377
+ test: {
378
+ name: 'test',
379
+ nodes: {
380
+ root: {
381
+ tag: 'div',
382
+ type: 'element',
383
+ attrs: {},
384
+ style: {
385
+ color: 'var(--new-theme-variable)',
358
386
  },
359
- formulas: {},
360
- apis: {},
361
- attributes: {},
362
- variables: {},
387
+ events: {},
388
+ classes: {},
389
+ children: [],
363
390
  },
364
391
  },
392
+ formulas: {},
393
+ apis: {},
394
+ attributes: {},
395
+ variables: {},
365
396
  },
397
+ },
398
+ }
399
+ const problems = Array.from(
400
+ searchProject({
401
+ files,
366
402
  rules: [unknownCSSVariableRule],
367
403
  }),
368
404
  )
369
405
 
370
- expect(problems).toBeEmpty()
406
+ expect(problems).toHaveLength(1)
407
+
408
+ const fixedProject = fixProject({
409
+ files,
410
+ rule: unknownCSSVariableRule,
411
+ fixType: 'add-to-theme',
412
+ })
413
+
414
+ expect(
415
+ Object.keys(fixedProject.themes?.Default.propertyDefinitions ?? {}),
416
+ ).toHaveLength(1)
417
+ expect(fixedProject.themes?.Default.propertyDefinitions).toHaveProperty(
418
+ '--new-theme-variable',
419
+ )
420
+ expect(
421
+ fixedProject.themes?.Default.propertyDefinitions?.[
422
+ '--new-theme-variable'
423
+ ],
424
+ ).toEqual({
425
+ syntax: {
426
+ type: 'primitive',
427
+ name: '*',
428
+ },
429
+ inherits: true,
430
+ initialValue: '',
431
+ values: {},
432
+ description: '',
433
+ })
371
434
  })
372
435
  })
@@ -1,10 +1,14 @@
1
- import type { Rule } from '../../../types'
1
+ import type { CustomPropertyDefinition } from '@nordcraft/core/dist/styling/theme'
2
+ import type { FixFunction, Rule, StyleNode } from '../../../types'
2
3
 
3
4
  const REGEX = /var\(\s*(--[\w-]+)/g
4
5
 
5
- export const unknownCSSVariableRule: Rule<{
6
- name: string
7
- }> = {
6
+ export const unknownCSSVariableRule: Rule<
7
+ {
8
+ name: string
9
+ },
10
+ StyleNode
11
+ > = {
8
12
  code: 'unknown css variable',
9
13
  level: 'warning',
10
14
  category: 'Unknown Reference',
@@ -30,69 +34,120 @@ export const unknownCSSVariableRule: Rule<{
30
34
  return
31
35
  }
32
36
 
33
- const allCssVariableDeclarations = memo(
34
- `all-css-variables-declarations`,
37
+ const themeCssVariables = theme.propertyDefinitions
38
+ const [_fileType, componentName, _nodes, nodeName] = path
39
+ const localCssVariables = memo(
40
+ `component-css-variables-${componentName}-${nodeName}`,
35
41
  () => {
36
- const cssVariableKeys = new Set(
37
- Object.keys(theme.propertyDefinitions ?? {}),
38
- )
39
- Object.values(files.components ?? {}).forEach((component) => {
40
- Object.keys(component?.nodes ?? {}).forEach((nodeKey) => {
41
- const node = component?.nodes?.[nodeKey]
42
- if (!node) {
43
- return
44
- }
42
+ const vars = new Set<string>()
43
+ const component = files.components[componentName]
44
+ if (!component) {
45
+ return vars
46
+ }
47
+
48
+ const visitVars = (nodeName: string) => {
49
+ const node = component?.nodes?.[nodeName]
50
+ if (!node) {
51
+ return
52
+ }
45
53
 
46
- if (node.type === 'component' || node.type === 'element') {
47
- Object.keys(node.customProperties ?? {}).forEach((varName) => {
48
- cssVariableKeys.add(varName)
54
+ if (node.type === 'component' || node.type === 'element') {
55
+ Object.keys(node.customProperties ?? {}).forEach((varName) => {
56
+ vars.add(varName)
57
+ })
58
+ Object.values(node.variants ?? {}).forEach((variant) => {
59
+ Object.keys(variant.customProperties ?? {}).forEach((varName) => {
60
+ vars.add(varName)
49
61
  })
50
- Object.values(node.variants ?? {}).forEach((variant) => {
51
- Object.keys(variant.customProperties ?? {}).forEach(
52
- (varName) => {
53
- cssVariableKeys.add(varName)
54
- },
55
- )
62
+ })
63
+
64
+ // Also add legacy style variables
65
+ if (node.type === 'element' && node['style-variables']) {
66
+ node['style-variables'].forEach((styleVar) => {
67
+ vars.add(`--${styleVar.name}`)
56
68
  })
69
+ }
57
70
 
58
- // Also add legacy style variables
59
- if (node.type === 'element' && node['style-variables']) {
60
- node['style-variables'].forEach((styleVar) => {
61
- cssVariableKeys.add(`--${styleVar.name}`)
62
- })
71
+ // Add if declared in any parent styles object
72
+ Object.keys(node.style ?? {}).forEach((styleKey) => {
73
+ if (styleKey.startsWith('--')) {
74
+ vars.add(styleKey)
63
75
  }
64
-
65
- // Add if declared in any parent styles object
66
- Object.keys(node.style ?? {}).forEach((styleKey) => {
76
+ })
77
+ Object.values(node.variants ?? {}).forEach((variant) => {
78
+ Object.keys(variant.style ?? {}).forEach((styleKey) => {
67
79
  if (styleKey.startsWith('--')) {
68
- cssVariableKeys.add(styleKey)
80
+ vars.add(styleKey)
69
81
  }
70
82
  })
71
- Object.values(node.variants ?? {}).forEach((variant) => {
72
- Object.keys(variant.style ?? {}).forEach((styleKey) => {
73
- if (styleKey.startsWith('--')) {
74
- cssVariableKeys.add(styleKey)
75
- }
76
- })
77
- })
78
- }
79
- })
80
- })
81
- return cssVariableKeys
83
+ })
84
+ }
85
+
86
+ const parent = Object.entries(component.nodes ?? {}).find(([_, n]) =>
87
+ n.children?.includes(nodeName),
88
+ )
89
+ if (parent) {
90
+ visitVars(parent[0])
91
+ }
92
+ }
93
+
94
+ visitVars(nodeName.toString())
95
+ return vars
82
96
  },
83
97
  )
84
98
 
85
99
  for (const varName of vars) {
86
- if (allCssVariableDeclarations.has(varName) === false) {
100
+ if (!(varName in themeCssVariables) && !localCssVariables.has(varName)) {
87
101
  report({
88
102
  path,
89
103
  info: {
90
104
  title: `Unknown CSS variable`,
91
- description: `The CSS variable **${varName}** is not declared in any parent element or in your theme. The CSS variable must be declared in an ancestor element in its component or in your global theme.`,
105
+ description: `The CSS variable **${varName}** is not declared in any parent element or theme. CSS variables must be declared in an ancestor element or in your global theme.`,
92
106
  },
93
107
  details: { name: varName },
108
+ fixes: ['add-to-theme'],
94
109
  })
95
110
  }
96
111
  }
97
112
  },
113
+ fixes: {
114
+ 'add-to-theme': addToThemeFix,
115
+ },
116
+ }
117
+
118
+ export type AddToThemeFix = 'add-to-theme'
119
+
120
+ function addToThemeFix(
121
+ args: Parameters<FixFunction<StyleNode, { name: string }>>[0],
122
+ ): ReturnType<FixFunction<StyleNode, { name: string }>> {
123
+ const varName = args.details?.name
124
+ if (typeof varName !== 'string' || !args.data.files.themes?.Default) {
125
+ return args.data.files
126
+ }
127
+
128
+ const definition: CustomPropertyDefinition = {
129
+ syntax: {
130
+ type: 'primitive',
131
+ name: '*',
132
+ },
133
+ inherits: true,
134
+ initialValue: '',
135
+ values: {},
136
+ description: '',
137
+ } as const
138
+
139
+ // Add the variable to the theme with Any (*) syntax type
140
+ return {
141
+ ...args.data.files,
142
+ themes: {
143
+ ...args.data.files.themes,
144
+ Default: {
145
+ ...args.data.files.themes.Default,
146
+ propertyDefinitions: {
147
+ ...args.data.files.themes.Default.propertyDefinitions,
148
+ [varName]: definition,
149
+ },
150
+ },
151
+ },
152
+ }
98
153
  }
@@ -210,6 +210,29 @@ export function* searchProject({
210
210
  state,
211
211
  fixOptions: fixOptions as any,
212
212
  })
213
+
214
+ if (files.packages) {
215
+ for (const key in files.packages) {
216
+ const pkg = files.packages[key]
217
+ if (pkg) {
218
+ yield* visitNode({
219
+ args: {
220
+ nodeType: 'project-package',
221
+ value: pkg,
222
+ packageName: key,
223
+ path: ['packages', key],
224
+ rules,
225
+ files,
226
+ pathsToVisit,
227
+ useExactPaths,
228
+ memo,
229
+ },
230
+ state,
231
+ fixOptions: fixOptions as any,
232
+ })
233
+ }
234
+ }
235
+ }
213
236
  }
214
237
 
215
238
  function visitNode(args: {
@@ -345,6 +368,7 @@ function* visitNode({
345
368
  case 'project-config':
346
369
  case 'project-theme':
347
370
  case 'project-theme-property':
371
+ case 'project-package':
348
372
  case 'style-declaration':
349
373
  case 'style-variable':
350
374
  case 'style-variant':
package/src/types.ts CHANGED
@@ -27,6 +27,7 @@ import type { StyleVariant } from '@nordcraft/core/dist/styling/variantSelector'
27
27
  import type { Nullable, PluginAction } from '@nordcraft/core/dist/types'
28
28
  import type {
29
29
  ApiService,
30
+ InstalledPackage,
30
31
  ProjectFiles,
31
32
  Route,
32
33
  ToddleProject,
@@ -50,8 +51,10 @@ import type { NoReferenceComponentFormulaRuleFix } from './rules/issues/formulas
50
51
  import type { NoReferenceProjectFormulaRuleFix } from './rules/issues/formulas/noReferenceProjectFormulaRule'
51
52
  import type { NoStaticNodeConditionRuleFix } from './rules/issues/logic/noStaticNodeCondition'
52
53
  import type { NoReferenceNodeRuleFix } from './rules/issues/miscellaneous/noReferenceNodeRule'
54
+ import type { NoReferenceProjectPackageRuleFix } from './rules/issues/miscellaneous/noReferencePackageRule'
53
55
  import type { InvalidStyleSyntaxRuleFix } from './rules/issues/style/invalidStyleSyntaxRule'
54
56
  import type { LegacyStyleVariableRuleFix } from './rules/issues/style/legacyStyleVariableRule'
57
+ import type { AddToThemeFix } from './rules/issues/style/unknownCSSVariable'
55
58
  import type { NoReferenceVariableRuleFix } from './rules/issues/variables/noReferenceVariableRule'
56
59
  import type { NoPostNavigateActionRuleFix } from './rules/issues/workflows/noPostNavigateAction'
57
60
 
@@ -90,6 +93,7 @@ export type Code =
90
93
  | 'no-reference node'
91
94
  | 'no-reference project action'
92
95
  | 'no-reference project formula'
96
+ | 'no-reference project package'
93
97
  | 'no-reference variable'
94
98
  | 'no-static-node-condition'
95
99
  | 'no-unnecessary-condition-falsy'
@@ -375,6 +379,12 @@ export type ProjectConfigNode = {
375
379
  value: unknown
376
380
  } & Base
377
381
 
382
+ export type ProjectPackageNode = {
383
+ nodeType: 'project-package'
384
+ value: InstalledPackage
385
+ packageName: string
386
+ } & Base
387
+
378
388
  export type StyleVariantNode = {
379
389
  nodeType: 'style-variant'
380
390
  value: {
@@ -434,11 +444,13 @@ export type NodeType =
434
444
  | ProjectRoute
435
445
  | ProjectThemeNode
436
446
  | ProjectThemePropertyNode
447
+ | ProjectPackageNode
437
448
  | StyleNode
438
449
  | StyleVariableNode
439
450
  | StyleVariantNode
440
451
 
441
452
  export type FixType =
453
+ | AddToThemeFix
442
454
  | ChangeDataTypeFix
443
455
  | DeleteFetchInputFix
444
456
  | InvalidStyleSyntaxRuleFix
@@ -461,6 +473,7 @@ export type FixType =
461
473
  | UnknownActionEventRuleFix
462
474
  | UnknownApiServiceRuleFix
463
475
  | UnknownComponentAttributeRuleFix
476
+ | NoReferenceProjectPackageRuleFix
464
477
 
465
478
  interface ReportedIssueInfo {
466
479
  title: string