@open-mercato/ui 0.8.1-develop.7218.1.9f22882799 → 0.8.1-develop.7219.1.f96cb57e70

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/build.mjs CHANGED
@@ -1,22 +1,49 @@
1
- import { writeFileSync } from 'node:fs'
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
2
  import { dirname, join } from 'node:path'
3
3
  import { fileURLToPath } from 'node:url'
4
4
  import { buildPackage } from '../../scripts/build-package.mjs'
5
5
  import { discoverResolvedIcons } from './scripts/lucideIconDiscovery.cjs'
6
- import { buildLucideRegistrySource } from './scripts/lucideRegistrySource.cjs'
6
+ import { buildLucideRegistrySource, checkLucideRegistryDrift } from './scripts/lucideRegistrySource.cjs'
7
7
 
8
8
  const packageDir = dirname(fileURLToPath(import.meta.url))
9
+ // `--check` regenerates the lucide registry in memory and fails on drift
10
+ // without writing or building — the CI guard mode (`yarn ui:icons:check`).
11
+ const checkOnly = process.argv.includes('--check')
9
12
 
10
13
  async function generateLucideRegistry() {
11
14
  const repoRoot = join(packageDir, '..', '..')
12
15
  const resolved = await discoverResolvedIcons(repoRoot)
13
16
 
14
17
  const outPath = join(packageDir, 'src/backend/icons/lucideRegistry.generated.tsx')
15
- writeFileSync(outPath, buildLucideRegistrySource(resolved))
18
+ const source = buildLucideRegistrySource(resolved)
19
+
20
+ // `--check` makes this a drift guard instead of a writer. The committed
21
+ // registry is a canonical mirror of a repo-wide scan, so nothing inside
22
+ // `packages/ui`'s own test suite can protect it: a PR that adds
23
+ // `icon: 'some-lucide-name'` in another package never selects `packages/ui`
24
+ // under turbo's `--filter=[base]...` (ui is a dependency of those packages,
25
+ // not a dependent), which is exactly how #4391 drifted unnoticed. CI calls
26
+ // this unfiltered, mirroring `ds:tokens:check`.
27
+ if (checkOnly) {
28
+ const committed = existsSync(outPath) ? readFileSync(outPath, 'utf8') : ''
29
+ const drift = checkLucideRegistryDrift(committed, source, outPath)
30
+ if (drift.drifted) {
31
+ console.error(drift.message)
32
+ process.exit(1)
33
+ }
34
+ console.log(`Lucide registry is in sync (${resolved.length} icons).`)
35
+ return
36
+ }
37
+
38
+ writeFileSync(outPath, source)
16
39
  console.log(`Generated lucide registry with ${resolved.length} icons -> ${outPath}`)
17
40
  }
18
41
 
19
- await buildPackage(packageDir, {
20
- name: 'ui',
21
- beforeBuild: generateLucideRegistry,
22
- })
42
+ if (checkOnly) {
43
+ await generateLucideRegistry()
44
+ } else {
45
+ await buildPackage(packageDir, {
46
+ name: 'ui',
47
+ beforeBuild: generateLucideRegistry,
48
+ })
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.8.1-develop.7218.1.9f22882799",
3
+ "version": "0.8.1-develop.7219.1.f96cb57e70",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -155,14 +155,14 @@
155
155
  "remark-gfm": "^4.0.1"
156
156
  },
157
157
  "peerDependencies": {
158
- "@open-mercato/shared": "0.8.1-develop.7218.1.9f22882799",
158
+ "@open-mercato/shared": "0.8.1-develop.7219.1.f96cb57e70",
159
159
  "react": ">=18.0.0",
160
160
  "react-dom": ">=18.0.0",
161
161
  "react-is": ">=18.0.0"
162
162
  },
163
163
  "devDependencies": {
164
164
  "@figma/code-connect": "^1.3.4",
165
- "@open-mercato/shared": "0.8.1-develop.7218.1.9f22882799",
165
+ "@open-mercato/shared": "0.8.1-develop.7219.1.f96cb57e70",
166
166
  "@testing-library/dom": "^10.4.1",
167
167
  "@testing-library/jest-dom": "^7.0.1",
168
168
  "@testing-library/react": "^16.3.3",
@@ -130,4 +130,19 @@ function buildLucideRegistrySource(resolvedIcons) {
130
130
  return normalizeSourceText(sourceFile.getFullText())
131
131
  }
132
132
 
133
- module.exports = { buildLucideRegistrySource }
133
+ // Drift decision for the committed registry, kept here rather than inline in
134
+ // build.mjs so it is reachable from Jest: build.mjs's top level awaits a full
135
+ // package build, so nothing can import it to exercise this comparison. The
136
+ // caller owns reporting and the exit code; this only decides.
137
+ function checkLucideRegistryDrift(committedSource, generatedSource, registryPath = GENERATED_FILE_NAME) {
138
+ if (committedSource === generatedSource) return { drifted: false }
139
+
140
+ return {
141
+ drifted: true,
142
+ message: `Lucide icon registry is out of date: ${registryPath}\n`
143
+ + 'A referenced icon name was added or removed without regenerating it.\n'
144
+ + 'Run `yarn workspace @open-mercato/ui build` and commit the result.',
145
+ }
146
+ }
147
+
148
+ module.exports = { buildLucideRegistrySource, checkLucideRegistryDrift }
@@ -4,7 +4,7 @@ import { tmpdir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
  import { Project, ScriptKind, SyntaxKind, type ObjectLiteralExpression, type SourceFile } from 'ts-morph'
6
6
  import { discoverResolvedIcons } from '../../../../scripts/lucideIconDiscovery.cjs'
7
- import { buildLucideRegistrySource } from '../../../../scripts/lucideRegistrySource.cjs'
7
+ import { buildLucideRegistrySource, checkLucideRegistryDrift } from '../../../../scripts/lucideRegistrySource.cjs'
8
8
  import jestConfig from '../../../../jest.config.cjs'
9
9
 
10
10
  const packageDir = join(__dirname, '..', '..', '..', '..')
@@ -272,6 +272,32 @@ describe('committed lucideRegistry.generated.tsx', () => {
272
272
  })
273
273
  })
274
274
 
275
+ // `yarn ui:icons:check` runs this decision unfiltered in CI, where only the in-sync
276
+ // branch is ever exercised — every PR runs it against a registry that matches. The
277
+ // branch that does the work is the drifted one, so it is asserted here instead.
278
+ describe('checkLucideRegistryDrift', () => {
279
+ const source = buildLucideRegistrySource(populatedFixture)
280
+
281
+ it('reports no drift when the committed file matches the emitter', () => {
282
+ expect(checkLucideRegistryDrift(source, source)).toEqual({ drifted: false })
283
+ })
284
+
285
+ it('reports drift when an icon is missing from the committed file', () => {
286
+ const stale = buildLucideRegistrySource(populatedFixture.slice(0, -1))
287
+ expect(checkLucideRegistryDrift(stale, source).drifted).toBe(true)
288
+ })
289
+
290
+ it('reports drift when the committed file has never been written', () => {
291
+ expect(checkLucideRegistryDrift('', source).drifted).toBe(true)
292
+ })
293
+
294
+ it('names the drifted file and the command that regenerates it', () => {
295
+ const drift = checkLucideRegistryDrift('', source, '/repo/lucideRegistry.generated.tsx')
296
+ expect(drift.message).toContain('/repo/lucideRegistry.generated.tsx')
297
+ expect(drift.message).toContain('yarn workspace @open-mercato/ui build')
298
+ })
299
+ })
300
+
275
301
  describe('jest transform scope', () => {
276
302
  const transformPatterns = Object.keys(jestConfig.transform).map((key) => new RegExp(key))
277
303