@dfosco/storyboard-react 2.0.0 → 2.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.
@@ -69,10 +69,13 @@ describe('storyboardDataPlugin', () => {
69
69
  const code = plugin.load(RESOLVED_ID)
70
70
 
71
71
  expect(code).toContain("import { init } from '@dfosco/storyboard-core'")
72
- expect(code).toContain('init({ scenes, objects, records })')
72
+ expect(code).toContain('init({ flows, objects, records, prototypes })')
73
73
  expect(code).toContain('"Test"')
74
74
  expect(code).toContain('"Jane"')
75
75
  expect(code).toContain('"First"')
76
+ // Backward-compat alias
77
+ expect(code).toContain('const scenes = flows')
78
+ expect(code).toContain('export { flows, scenes, objects, records, prototypes }')
76
79
  })
77
80
 
78
81
  it('load returns null for other IDs', () => {
@@ -93,7 +96,24 @@ describe('storyboardDataPlugin', () => {
93
96
  )
94
97
 
95
98
  const plugin = createPlugin()
96
- expect(() => plugin.load(RESOLVED_ID)).toThrow(/Duplicate data file/)
99
+ expect(() => plugin.load(RESOLVED_ID)).toThrow(/Duplicate flow "dup"/)
100
+ })
101
+
102
+ it('duplicate objects show globally-scoped hint', () => {
103
+ mkdirSync(path.join(tmpDir, 'src', 'data'), { recursive: true })
104
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Dashboard'), { recursive: true })
105
+ writeFileSync(
106
+ path.join(tmpDir, 'src', 'data', 'user.object.json'),
107
+ JSON.stringify({ name: 'Global' }),
108
+ )
109
+ writeFileSync(
110
+ path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'user.object.json'),
111
+ JSON.stringify({ name: 'Local' }),
112
+ )
113
+
114
+ const plugin = createPlugin()
115
+ expect(() => plugin.load(RESOLVED_ID)).toThrow(/Duplicate object "user"/)
116
+ expect(() => plugin.load(RESOLVED_ID)).toThrow(/globally scoped/)
97
117
  })
98
118
 
99
119
  it('handles JSONC files (with comments)', () => {
@@ -107,6 +127,19 @@ describe('storyboardDataPlugin', () => {
107
127
  expect(code).toContain('"JSONC Scene"')
108
128
  })
109
129
 
130
+ it('normalizes .scene files into flow category in the index', () => {
131
+ writeFileSync(
132
+ path.join(tmpDir, 'legacy.scene.json'),
133
+ JSON.stringify({ title: 'Legacy Scene' }),
134
+ )
135
+ const plugin = createPlugin()
136
+ const code = plugin.load(RESOLVED_ID)
137
+
138
+ // .scene.json files should be normalized to the flows category
139
+ expect(code).toContain('"Legacy Scene"')
140
+ expect(code).toContain('init({ flows, objects, records, prototypes })')
141
+ })
142
+
110
143
  it('buildStart resets the index cache', () => {
111
144
  writeDataFiles(tmpDir)
112
145
  const plugin = createPlugin()
@@ -131,3 +164,103 @@ describe('storyboardDataPlugin', () => {
131
164
  expect(code3).toContain('"Extra"')
132
165
  })
133
166
  })
167
+
168
+ describe('prototype scoping', () => {
169
+ it('prefixes flows inside src/prototypes/{Name}/ with the prototype name', () => {
170
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Dashboard'), { recursive: true })
171
+ writeFileSync(
172
+ path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'default.flow.json'),
173
+ JSON.stringify({ title: 'Dashboard Default' }),
174
+ )
175
+ writeFileSync(
176
+ path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'signup.flow.json'),
177
+ JSON.stringify({ title: 'Dashboard Signup' }),
178
+ )
179
+ // Global flow in src/data/
180
+ mkdirSync(path.join(tmpDir, 'src', 'data'), { recursive: true })
181
+ writeFileSync(
182
+ path.join(tmpDir, 'src', 'data', 'default.flow.json'),
183
+ JSON.stringify({ title: 'Global Default' }),
184
+ )
185
+
186
+ const plugin = createPlugin()
187
+ const code = plugin.load(RESOLVED_ID)
188
+
189
+ expect(code).toContain('"Dashboard/default"')
190
+ expect(code).toContain('"Dashboard/signup"')
191
+ expect(code).toContain('"default"')
192
+ expect(code).toContain('"Dashboard Default"')
193
+ expect(code).toContain('"Global Default"')
194
+ })
195
+
196
+ it('prefixes records inside src/prototypes/{Name}/ with the prototype name', () => {
197
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Blog'), { recursive: true })
198
+ writeFileSync(
199
+ path.join(tmpDir, 'src', 'prototypes', 'Blog', 'posts.record.json'),
200
+ JSON.stringify([{ id: '1', title: 'Scoped Post' }]),
201
+ )
202
+ // Global record
203
+ mkdirSync(path.join(tmpDir, 'src', 'data'), { recursive: true })
204
+ writeFileSync(
205
+ path.join(tmpDir, 'src', 'data', 'posts.record.json'),
206
+ JSON.stringify([{ id: '1', title: 'Global Post' }]),
207
+ )
208
+
209
+ const plugin = createPlugin()
210
+ const code = plugin.load(RESOLVED_ID)
211
+
212
+ expect(code).toContain('"Blog/posts"')
213
+ expect(code).toContain('"posts"')
214
+ expect(code).toContain('"Scoped Post"')
215
+ expect(code).toContain('"Global Post"')
216
+ })
217
+
218
+ it('does NOT prefix objects inside src/prototypes/{Name}/', () => {
219
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Dashboard'), { recursive: true })
220
+ writeFileSync(
221
+ path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'helpers.object.json'),
222
+ JSON.stringify({ util: true }),
223
+ )
224
+
225
+ const plugin = createPlugin()
226
+ const code = plugin.load(RESOLVED_ID)
227
+
228
+ // Object should be plain "helpers", NOT "Dashboard/helpers"
229
+ expect(code).toContain('"helpers"')
230
+ expect(code).not.toContain('"Dashboard/helpers"')
231
+ })
232
+
233
+ it('allows same flow name in different prototypes without clash', () => {
234
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'A'), { recursive: true })
235
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'B'), { recursive: true })
236
+ writeFileSync(
237
+ path.join(tmpDir, 'src', 'prototypes', 'A', 'default.flow.json'),
238
+ JSON.stringify({ from: 'A' }),
239
+ )
240
+ writeFileSync(
241
+ path.join(tmpDir, 'src', 'prototypes', 'B', 'default.flow.json'),
242
+ JSON.stringify({ from: 'B' }),
243
+ )
244
+
245
+ const plugin = createPlugin()
246
+ // Should not throw (no duplicate)
247
+ const code = plugin.load(RESOLVED_ID)
248
+ expect(code).toContain('"A/default"')
249
+ expect(code).toContain('"B/default"')
250
+ })
251
+
252
+ it('normalizes .scene.json inside prototypes to scoped flow', () => {
253
+ mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Legacy'), { recursive: true })
254
+ writeFileSync(
255
+ path.join(tmpDir, 'src', 'prototypes', 'Legacy', 'old.scene.json'),
256
+ JSON.stringify({ compat: true }),
257
+ )
258
+
259
+ const plugin = createPlugin()
260
+ const code = plugin.load(RESOLVED_ID)
261
+
262
+ // Should be indexed as a scoped flow, not a scene
263
+ expect(code).toContain('"Legacy/old"')
264
+ expect(code).toContain('flows')
265
+ })
266
+ })