···11+import fs from 'node:fs';
12import path from 'node:path';
33+import { fileURLToPath } from 'node:url';
2435import { $RefParser } from '..';
46import { getSpecsPath } from './utils';
5788+const __filename = fileURLToPath(import.meta.url);
99+const __dirname = path.dirname(__filename);
1010+1111+const getSnapshotsPath = () => path.join(__dirname, '__snapshots__');
1212+const getTempSnapshotsPath = () => path.join(__dirname, '.gen', 'snapshots');
1313+1414+/**
1515+ * Helper function to compare a bundled schema with a snapshot file.
1616+ * Handles writing the schema to a temp file and comparing with the snapshot.
1717+ *
1818+ * @param schema - The bundled schema to compare
1919+ * @param snapshotName - The name of the snapshot file (e.g., 'circular-ref-with-description.json')
2020+ */
2121+const expectBundledSchemaToMatchSnapshot = async (schema: unknown, snapshotName: string) => {
2222+ const outputPath = path.join(getTempSnapshotsPath(), snapshotName);
2323+ const snapshotPath = path.join(getSnapshotsPath(), snapshotName);
2424+2525+ // Ensure directory exists
2626+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
2727+2828+ // Write the bundled result
2929+ const content = JSON.stringify(schema, null, 2);
3030+ fs.writeFileSync(outputPath, content);
3131+3232+ // Compare with snapshot
3333+ await expect(content).toMatchFileSnapshot(snapshotPath);
3434+};
3535+636describe('bundle', () => {
737 it('handles circular reference with description', async () => {
838 const refParser = new $RefParser();
···1242 'circular-ref-with-description.json',
1343 );
1444 const schema = await refParser.bundle({ pathOrUrlOrSchema });
1515- expect(schema).toEqual({
1616- schemas: {
1717- Bar: {
1818- $ref: '#/schemas/Foo',
1919- description: 'ok',
2020- },
2121- Foo: {
2222- $ref: '#/schemas/Bar',
2323- },
2424- },
2525- });
4545+4646+ await expectBundledSchemaToMatchSnapshot(schema, 'circular-ref-with-description.json');
2647 });
27482849 it('bundles multiple references to the same file correctly', async () => {
···3253 'json-schema-ref-parser',
3354 'multiple-refs.json',
3455 );
3535- const schema = (await refParser.bundle({ pathOrUrlOrSchema })) as any;
5656+ const schema = await refParser.bundle({ pathOrUrlOrSchema });
36573737- // Both parameters should now be $ref to the same internal definition
3838- const firstParam = schema.paths['/test1/{pathId}'].get.parameters[0];
3939- const secondParam = schema.paths['/test2/{pathId}'].get.parameters[0];
5858+ await expectBundledSchemaToMatchSnapshot(schema, 'multiple-refs.json');
5959+ });
40604141- // The $ref should match the output structure in file_context_0
4242- expect(firstParam.$ref).toBe('#/components/parameters/path-parameter_pathId');
4343- expect(secondParam.$ref).toBe('#/components/parameters/path-parameter_pathId');
6161+ it('hoists sibling schemas from external files', async () => {
6262+ const refParser = new $RefParser();
6363+ const pathOrUrlOrSchema = path.join(
6464+ getSpecsPath(),
6565+ 'json-schema-ref-parser',
6666+ 'main-with-external-siblings.json',
6767+ );
6868+ const schema = await refParser.bundle({ pathOrUrlOrSchema });
6969+7070+ await expectBundledSchemaToMatchSnapshot(schema, 'main-with-external-siblings.json');
7171+ });
7272+7373+ it('hoists sibling schemas from YAML files with versioned names (Redfish-like)', async () => {
7474+ const refParser = new $RefParser();
7575+ const pathOrUrlOrSchema = path.join(
7676+ getSpecsPath(),
7777+ 'json-schema-ref-parser',
7878+ 'redfish-like.yaml',
7979+ );
8080+ const schema = await refParser.bundle({ pathOrUrlOrSchema });
44814545- // The referenced parameter should exist and match the expected structure
4646- expect(schema.components).toBeDefined();
4747- expect(schema.components.parameters).toBeDefined();
4848- expect(schema.components.parameters['path-parameter_pathId']).toEqual({
4949- in: 'path',
5050- name: 'pathId',
5151- required: true,
5252- schema: {
5353- description: 'Unique identifier for the path',
5454- format: 'uuid',
5555- type: 'string',
5656- },
5757- });
8282+ await expectBundledSchemaToMatchSnapshot(schema, 'redfish-like.json');
5883 });
5984});
+16-1
packages/json-schema-ref-parser/src/bundle.ts
···617617 } catch {
618618 // Ignore errors
619619 }
620620- const proposed = `${proposedBase}_${lastToken(entry.hash)}`;
620620+621621+ // Try without prefix first (cleaner names)
622622+ const schemaName = lastToken(entry.hash);
623623+ let proposed = schemaName;
624624+625625+ // Check if this name would conflict with existing schemas from other files
626626+ if (!usedNamesByObj.has(container)) {
627627+ usedNamesByObj.set(container, new Set<string>(Object.keys(container || {})));
628628+ }
629629+ const used = usedNamesByObj.get(container)!;
630630+631631+ // If the name is already used, add the file prefix
632632+ if (used.has(proposed)) {
633633+ proposed = `${proposedBase}_${schemaName}`;
634634+ }
635635+621636 defName = uniqueName(container, proposed);
622637 namesForPrefix.set(targetKey, defName);
623638 // Store the resolved value under the container