11import * as fs from 'fs' ;
22import * as process from 'process' ;
3+ import { createRequire } from 'module' ;
34import type { Optional } from '@stoplight/types' ;
4- import { Ruleset } from '@stoplight/spectral-core' ;
5+ import { Ruleset , RulesetDefinition } from '@stoplight/spectral-core' ;
56import { info , error } from '@actions/core' ;
7+ import { isError , isObject } from 'lodash' ;
68import * as path from '@stoplight/path' ;
79import { fetch } from '@stoplight/spectral-runtime' ;
8- import type { IO } from '@stoplight/spectral-ruleset-bundler' ;
10+ import { migrateRuleset , isBasicRuleset } from '@stoplight/spectral-ruleset-migrator' ;
11+ import { bundleRuleset } from '@stoplight/spectral-ruleset-bundler' ;
12+ import { node } from '@stoplight/spectral-ruleset-bundler/presets/node' ;
913import { builtins } from '@stoplight/spectral-ruleset-bundler/plugins/builtins' ;
1014import { commonjs } from '@stoplight/spectral-ruleset-bundler/plugins/commonjs' ;
11- import { bundleAndLoadRuleset } from '@stoplight/spectral-ruleset-bundler/with-loader ' ;
15+ import { stdin } from '@stoplight/spectral-ruleset-bundler/plugins/stdin ' ;
1216
1317async function getDefaultRulesetFile ( ) : Promise < Optional < string > > {
1418 const cwd = process . cwd ( ) ;
@@ -21,6 +25,10 @@ async function getDefaultRulesetFile(): Promise<Optional<string>> {
2125 return ;
2226}
2327
28+ function isErrorWithCode ( error : Error | ( Error & { code : unknown } ) ) : error is Error & { code : string } {
29+ return 'code' in error && typeof error . code === 'string' ;
30+ }
31+
2432export async function getRuleset ( rulesetFile : Optional < string > ) : Promise < Ruleset > {
2533 if ( ! rulesetFile ) {
2634 rulesetFile = await getDefaultRulesetFile ( ) ;
@@ -36,12 +44,63 @@ export async function getRuleset(rulesetFile: Optional<string>): Promise<Ruleset
3644
3745 info ( `Loading ruleset '${ rulesetFile } '...` ) ;
3846
39- const io : IO = { fetch , fs } ;
47+ let ruleset : string ;
4048
4149 try {
42- return await bundleAndLoadRuleset ( rulesetFile , io , [ builtins ( ) , commonjs ( ) ] ) ;
50+ if ( await isBasicRuleset ( rulesetFile ) ) {
51+ const migratedRuleset = await migrateRuleset ( rulesetFile , {
52+ format : 'esm' ,
53+ fs,
54+ } ) ;
55+
56+ rulesetFile = path . join ( path . dirname ( rulesetFile ) , '.spectral.js' ) ;
57+
58+ ruleset = await bundleRuleset ( rulesetFile , {
59+ target : 'node' ,
60+ format : 'commonjs' ,
61+ plugins : [ stdin ( migratedRuleset , rulesetFile ) , builtins ( ) , commonjs ( ) , ...node ( { fs, fetch } ) ] ,
62+ } ) ;
63+ } else {
64+ ruleset = await bundleRuleset ( rulesetFile , {
65+ target : 'node' ,
66+ format : 'commonjs' ,
67+ plugins : [ builtins ( ) , commonjs ( ) , ...node ( { fs, fetch } ) ] ,
68+ } ) ;
69+ }
70+
71+ return new Ruleset ( load ( ruleset , rulesetFile ) , {
72+ severity : 'recommended' ,
73+ source : rulesetFile ,
74+ } ) ;
4375 } catch ( e ) {
44- error ( `Failed to load ruleset '${ rulesetFile } '... Error: ${ String ( e ) } ` ) ;
76+ if ( ! isError ( e ) || ! isErrorWithCode ( e ) || e . code !== 'UNRESOLVED_ENTRY' ) {
77+ error ( `Could not load ${ rulesetFile } ruleset` ) ;
78+ } else {
79+ error ( `Could not load ${ rulesetFile } ruleset. ${ e . message } ` ) ;
80+ }
81+
4582 throw e ;
4683 }
4784}
85+
86+ function load ( source : string , uri : string ) : RulesetDefinition {
87+ const actualUri = path . isURL ( uri ) ? uri . replace ( / ^ h t t p s ? : \/ / , '' ) : uri ;
88+ // we could use plain `require`, but this approach has a number of benefits:
89+ // - it is bundler-friendly
90+ // - ESM compliant
91+ // - and we have no warning raised by pkg.
92+ const req = createRequire ( actualUri ) ;
93+ const m : { exports ?: RulesetDefinition } = { } ;
94+ const paths = [ path . dirname ( uri ) , __dirname ] ;
95+
96+ const _require = ( id : string ) : unknown => req ( req . resolve ( id , { paths } ) ) ;
97+
98+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
99+ Function ( 'module, require' , source ) ( m , _require ) ;
100+
101+ if ( ! isObject ( m . exports ) ) {
102+ throw new Error ( 'No valid export found' ) ;
103+ }
104+
105+ return m . exports ;
106+ }
0 commit comments