Repository: stoplightio/spectral
Pull request #2194 (2022-06-27 13:35:03)

feat(ruleset-bundler): add fullOutput option to bundleRuleset
Needed for https://github.com/stoplightio/platform-internal/pull/11817

**Checklist**

- [x] Tests added / updated
- [ ] Docs added / updated

**Does this PR introduce a breaking change?**

- [ ] Yes
- [x] No





Patch:
diff --git a/packages/ruleset-bundler/src/index.ts b/packages/ruleset-bundler/src/index.ts
index 38acec50..3a9fb6c4 100644
--- a/packages/ruleset-bundler/src/index.ts
+++ b/packages/ruleset-bundler/src/index.ts
@@ -1,4 +1,4 @@
-import { rollup, Plugin } from 'rollup';
+import { rollup, Plugin, OutputChunk } from 'rollup';
 import { isURL } from '@stoplight/path';
 import { isPackageImport } from './utils/isPackageImport';
 import { dedupeRollupPlugins } from './utils/dedupeRollupPlugins';
@@ -8,14 +8,23 @@ export type BundleOptions = {
   target: 'node' | 'browser' | 'runtime';
   format?: 'esm' | 'commonjs' | 'iife';
   treeshake?: boolean; // false by default
+  fullOutput?: boolean;
 };
 
 export { IO } from './types';
 
 export async function bundleRuleset(
   input: string,
-  { target = 'browser', plugins, format, treeshake = false }: BundleOptions,
-): Promise<string> {
+  opts: Omit<BundleOptions, 'fullOutput'> | (Omit<BundleOptions, 'fullOutput'> & { fullOutput: false }),
+): Promise<string>;
+export async function bundleRuleset(
+  input: string,
+  opts: Omit<BundleOptions, 'fullOutput'> & { fullOutput: true },
+): Promise<OutputChunk>;
+export async function bundleRuleset(
+  input: string,
+  { target = 'browser', plugins, format, treeshake = false, fullOutput = false }: BundleOptions,
+): Promise<string | OutputChunk> {
   const bundle = await rollup({
     input,
     plugins: dedupeRollupPlugins(plugins),
@@ -39,6 +48,12 @@ export async function bundleRuleset(
             id.startsWith('node:') || (!isURL(id) && isPackageImport(id) && (importer === void 0 || !isURL(importer))),
   });
 
-  return (await bundle.generate({ format: format ?? (target === 'runtime' ? 'iife' : 'esm'), exports: 'auto' }))
-    .output[0].code;
+  const chunks = (await bundle.generate({ format: format ?? (target === 'runtime' ? 'iife' : 'esm'), exports: 'auto' }))
+    .output;
+
+  if (fullOutput) {
+    return chunks[0];
+  }
+
+  return chunks[0].code;
 }


Test patch:
diff --git a/packages/ruleset-bundler/src/__tests__/index.test.ts b/packages/ruleset-bundler/src/__tests__/index.test.ts
index e257b53a..450343db 100644
--- a/packages/ruleset-bundler/src/__tests__/index.test.ts
+++ b/packages/ruleset-bundler/src/__tests__/index.test.ts
@@ -309,4 +309,79 @@ export { spectral as default };
 `);
     });
   });
+
+  it('given fullOutput set to true, should expose the entire built chunk', async () => {
+    serveAssets({
+      '/p/.spectral/my-fn.mjs': `import {isOdd} from './helpers/index.mjs';
+
+export default (input) => {
+  if (!isOdd(input)) {
+    return [{ message: 'must be odd' }];
+  }
+};`,
+
+      'https://cdn.skypack.dev/lodash.uppercase': `export * from '/-/lodash.uppercase@v4.3.0-Ghj8UDzvgbRFVHwnUM53/dist=es2020,mode=imports/optimized/lodash.uppercase.js';
+export {default} from '/-/lodash.uppercase@v4.3.0-Ghj8UDzvgbRFVHwnUM53/dist=es2020,mode=imports/optimized/lodash.uppercase.js';`,
+
+      'https://cdn.skypack.dev/-/lodash.uppercase@v4.3.0-Ghj8UDzvgbRFVHwnUM53/dist=es2020,mode=imports/optimized/lodash.uppercase.js':
+        'export default (a) => a.toUpperCase()',
+
+      '/p/.spectral/upper-case.mjs': `import upperCase from 'https://cdn.skypack.dev/lodash.uppercase';
+
+export default (input) => {
+  if (upperCase(input) !== input) {
+    return [{ message: 'must be upper case' }];
+  }
+};`,
+
+      '/p/.spectral/lower-case.mjs': `import lowerCase from 'lodash.lowercase';
+
+export default (input) => {
+  if (lowerCase(input) !== input) {
+    return [{ message: 'must be lower case' }];
+  }
+};`,
+
+      '/p/.spectral/helpers/index.mjs': `export * from './is-odd.mjs';`,
+      '/p/.spectral/helpers/is-odd.mjs': `export const isOdd = (value) => value % 2 === 1`,
+
+      '/p/spectral.mjs': `import myFn from './.spectral/my-fn.mjs';
+import lowerCase from './.spectral/lower-case.mjs';
+import upperCase from './.spectral/upper-case.mjs';
+
+export default {
+  rules: {
+    'odd-rule': {
+       given: '$',
+       then: { function: myFn },
+    },
+    'upper-case-rule': {
+       given: '$',
+       then: { function: upperCase },
+    },
+    'lower-case-rule': {
+       given: '$',
+       then: { function: lowerCase },
+    },
+  },
+};`,
+    });
+
+    const bundle = await bundleRuleset('/p/spectral.mjs', {
+      target: 'node',
+      plugins: [...node(io), virtualFs(io)],
+      fullOutput: true,
+    });
+
+    expect(Object.keys(bundle.modules).sort()).toStrictEqual([
+      '/p/.spectral/helpers/index.mjs',
+      '/p/.spectral/helpers/is-odd.mjs',
+      '/p/.spectral/lower-case.mjs',
+      '/p/.spectral/my-fn.mjs',
+      '/p/.spectral/upper-case.mjs',
+      '/p/spectral.mjs',
+      'https://cdn.skypack.dev/-/lodash.uppercase@v4.3.0-Ghj8UDzvgbRFVHwnUM53/dist=es2020,mode=imports/optimized/lodash.uppercase.js',
+      'https://cdn.skypack.dev/lodash.uppercase',
+    ]);
+  });
 });