diff --git a/packages/ruleset-migrator/package.json b/packages/ruleset-migrator/package.json index 43df39b812..1e2b2b05d6 100644 --- a/packages/ruleset-migrator/package.json +++ b/packages/ruleset-migrator/package.json @@ -21,7 +21,7 @@ "url": "https://github.com/stoplightio/spectral.git" }, "dependencies": { - "@stoplight/json": "3.17.0", + "@stoplight/json": "~3.17.0", "@stoplight/ordered-object-literal": "1.0.2", "@stoplight/path": "1.3.2", "@stoplight/spectral-functions": "^1.0.0", diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.cjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.cjs index 12891d8d81..9cc61f982b 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.cjs +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.cjs @@ -6,9 +6,9 @@ module.exports = { extends: [ { rules: { - "my-rule": { - message: "ruleset 2", - given: "$", + 'my-rule': { + message: 'ruleset 2', + given: '$', then: { function: falsy, }, @@ -17,9 +17,9 @@ module.exports = { }, { rules: { - "my-rule": { - message: "ruleset 3", - given: "$", + 'my-rule': { + message: 'ruleset 3', + given: '$', then: { function: pascalCase, }, @@ -28,9 +28,9 @@ module.exports = { }, ], rules: { - "my-rule": { - message: "ruleset", - given: "$", + 'my-rule': { + message: 'ruleset', + given: '$', then: { function: truthy, }, @@ -40,5 +40,5 @@ module.exports = { ], }; function _interopDefault(ex) { - return ex && typeof ex === "object" && "default" in ex ? ex["default"] : ex; + return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex; } diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.mjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.mjs index 56e3591825..33e8fe2d86 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.mjs +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/extends-variant-8/output.mjs @@ -1,5 +1,5 @@ -import { falsy, truthy } from "@stoplight/spectral-functions"; -import pascalCase from "/.tmp/spectral/extends-variant-8/assets/shared/functions/pascalCase.js"; +import { falsy, truthy } from '@stoplight/spectral-functions'; +import pascalCase from '/.tmp/spectral/extends-variant-8/assets/shared/functions/pascalCase.js'; export default { extends: [ { diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.cjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.cjs index aaf98e740c..904b4ef2af 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.cjs +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.cjs @@ -1,13 +1,13 @@ -const { oas3, oas3_1 } = require('@stoplight/spectral-formats'); -const oas3$0 = _interopDefault(require('/.tmp/spectral/functions-variant-4/functions/oas3.js')); +const { oas3: oas3$0, oas3_1 } = require('@stoplight/spectral-formats'); +const oas3 = _interopDefault(require('/.tmp/spectral/functions-variant-4/functions/oas3.js')); module.exports = { - formats: [oas3, oas3_1], + formats: [oas3$0, oas3_1], rules: { rule: { given: '$', then: [ { - function: oas3$0, + function: oas3, }, ], }, diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.mjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.mjs index 0561856a47..6699691bff 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.mjs +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-4/output.mjs @@ -1,13 +1,13 @@ -import { oas3, oas3_1 } from '@stoplight/spectral-formats'; -import oas3$0 from '/.tmp/spectral/functions-variant-4/functions/oas3.js'; +import { oas3 as oas3$0, oas3_1 } from '@stoplight/spectral-formats'; +import oas3 from '/.tmp/spectral/functions-variant-4/functions/oas3.js'; export default { - formats: [oas3, oas3_1], + formats: [oas3$0, oas3_1], rules: { rule: { given: '$', then: [ { - function: oas3$0, + function: oas3, }, ], }, diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/output.cjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/output.cjs new file mode 100644 index 0000000000..5455b84d0b --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/output.cjs @@ -0,0 +1,14 @@ +const donothing = _interopDefault(require('/.tmp/spectral/functions-variant-5/custom-functions/do-nothing.js')); +module.exports = { + rules: { + rule: { + given: '$', + then: { + function: donothing, + }, + }, + }, +}; +function _interopDefault(ex) { + return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex; +} diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/output.mjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/output.mjs new file mode 100644 index 0000000000..86b1a247a3 --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/output.mjs @@ -0,0 +1,11 @@ +import donothing from '/.tmp/spectral/functions-variant-5/custom-functions/do-nothing.js'; +export default { + rules: { + rule: { + given: '$', + then: { + function: donothing, + }, + }, + }, +}; diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/ruleset.yaml b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/ruleset.yaml new file mode 100644 index 0000000000..440b085b74 --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-5/ruleset.yaml @@ -0,0 +1,8 @@ +rules: + rule: + given: $ + then: + function: do-nothing +functionsDir: custom-functions +functions: + - do-nothing diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/output.cjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/output.cjs new file mode 100644 index 0000000000..15a66e3412 --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/output.cjs @@ -0,0 +1,11 @@ +const { truthy } = require('@stoplight/spectral-functions'); +module.exports = { + rules: { + rule: { + given: '$', + then: { + function: truthy, + }, + }, + }, +}; diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/output.mjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/output.mjs new file mode 100644 index 0000000000..319df78e4e --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/output.mjs @@ -0,0 +1,11 @@ +import { truthy } from '@stoplight/spectral-functions'; +export default { + rules: { + rule: { + given: '$', + then: { + function: truthy, + }, + }, + }, +}; diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/ruleset.yaml b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/ruleset.yaml new file mode 100644 index 0000000000..1d6fba1cb2 --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-6/ruleset.yaml @@ -0,0 +1,6 @@ +rules: + rule: + given: $ + then: + function: truthy +functionsDir: custom-fns diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/output.cjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/output.cjs new file mode 100644 index 0000000000..262a10c6e8 --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/output.cjs @@ -0,0 +1,10 @@ +module.exports = { + rules: { + rule: { + given: '$', + then: { + function: void 0, + }, + }, + }, +}; diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/output.mjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/output.mjs new file mode 100644 index 0000000000..d40e734b52 --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/output.mjs @@ -0,0 +1,10 @@ +export default { + rules: { + rule: { + given: '$', + then: { + function: void 0, + }, + }, + }, +}; diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/ruleset.yaml b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/ruleset.yaml new file mode 100644 index 0000000000..6a52ee783e --- /dev/null +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/functions-variant-7/ruleset.yaml @@ -0,0 +1,5 @@ +rules: + rule: + given: $ + then: + function: do-nothing diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.cjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.cjs index 981ba5ab6e..786caeebb7 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.cjs +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.cjs @@ -1,4 +1,5 @@ const { oas2 } = require('@stoplight/spectral-formats'); +const { truthy } = require('@stoplight/spectral-functions'); module.exports = { rules: { 'oas3-schema': 'error', @@ -11,7 +12,7 @@ module.exports = { given: "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]", then: { - function: void 0, + function: truthy, functionOptions: null, }, }, diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.mjs b/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.mjs index 5bcb82491e..5373a77e16 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.mjs +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/output.mjs @@ -1,4 +1,5 @@ import { oas2 } from '@stoplight/spectral-formats'; +import { truthy } from '@stoplight/spectral-functions'; export default { rules: { 'oas3-schema': 'error', @@ -11,7 +12,7 @@ export default { given: "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]", then: { - function: void 0, + function: truthy, functionOptions: null, }, }, diff --git a/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/ruleset.yaml b/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/ruleset.yaml index 9ce991d8de..22a5b2fb83 100644 --- a/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/ruleset.yaml +++ b/packages/ruleset-migrator/src/__tests__/__fixtures__/rules-variant-1/ruleset.yaml @@ -12,5 +12,5 @@ rules: "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]" then: - function: oasOpFormDataConsumeCheck + function: truthy functionOptions: null diff --git a/packages/ruleset-migrator/src/index.ts b/packages/ruleset-migrator/src/index.ts index f09df49544..2c24542ae2 100644 --- a/packages/ruleset-migrator/src/index.ts +++ b/packages/ruleset-migrator/src/index.ts @@ -50,18 +50,18 @@ export async function migrateRuleset(filepath: string, opts: MigrationOptions): }; for (const transformer of transformers) { - transformer(ctx); + transformer((...hook) => void ctx.hooks.add(hook)); } - tree.ruleset = await process(ruleset, hooks); + tree.ruleset = await process(ruleset, ctx); return tree.toString(); } -async function _process(input: unknown, hooks: Set, path: string): Promise { - for (const [pattern, fn] of hooks) { +async function _process(input: unknown, ctx: TransformerCtx, path: string): Promise { + for (const [pattern, fn] of ctx.hooks) { if (pattern.test(path)) { - const output = await fn(input); + const output = await fn(input, ctx); if (output !== void 0) { return output; @@ -71,7 +71,9 @@ async function _process(input: unknown, hooks: Set, path: string): Promise if (Array.isArray(input)) { return b.arrayExpression( - (await Promise.all(input.map((item, i) => _process(item, hooks, `${path}/${String(i)}`)))).filter(Boolean), + (await Promise.all(input.map(async (item, i) => await _process(item, ctx, `${path}/${String(i)}`)))).filter( + Boolean, + ), ); } else if (typeof input === 'number' || typeof input === 'boolean' || typeof input === 'string') { return b.literal(input); @@ -87,7 +89,7 @@ async function _process(input: unknown, hooks: Set, path: string): Promise ( await Promise.all( Object.entries(input).map(async ([key, value]) => { - const propertyValue = await _process(value, hooks, `${path}/${key}`); + const propertyValue = await _process(value, ctx, `${path}/${key}`); if (propertyValue !== null) { return b.property('init', b.identifier(JSON.stringify(key)), propertyValue); @@ -100,6 +102,6 @@ async function _process(input: unknown, hooks: Set, path: string): Promise ); } -export async function process(input: Ruleset, hooks: Set): Promise { - return (await _process(input, hooks, '')) as namedTypes.ObjectExpression; +export async function process(input: Ruleset, ctx: TransformerCtx): Promise { + return (await _process(input, ctx, '')) as namedTypes.ObjectExpression; } diff --git a/packages/ruleset-migrator/src/transformers/except.ts b/packages/ruleset-migrator/src/transformers/except.ts index f5db9a90be..933c0552ea 100644 --- a/packages/ruleset-migrator/src/transformers/except.ts +++ b/packages/ruleset-migrator/src/transformers/except.ts @@ -3,27 +3,24 @@ import { Ruleset } from '../validation/types'; export { transformer as default }; -const transformer: Transformer = function (ctx) { - ctx.hooks.add([ - /^$/, - (_ruleset): void => { - const ruleset = _ruleset as Ruleset; - const { except } = ruleset; +const transformer: Transformer = function (registerHook) { + registerHook(/^$/, (_ruleset): void => { + const ruleset = _ruleset as Ruleset; + const { except } = ruleset; - if (except === void 0) return; + if (except === void 0) return; - delete ruleset.except; + delete ruleset.except; - const overrides = (ruleset.overrides ??= []) as unknown[]; - overrides.push( - ...Object.keys(except).map(pattern => ({ - files: [pattern.startsWith('#') ? `**${pattern}` : pattern], - rules: except[pattern].reduce((rules, rule) => { - rules[rule] = 'off'; - return rules; - }, {}), - })), - ); - }, - ]); + const overrides = (ruleset.overrides ??= []) as unknown[]; + overrides.push( + ...Object.keys(except).map(pattern => ({ + files: [pattern.startsWith('#') ? `**${pattern}` : pattern], + rules: except[pattern].reduce((rules, rule) => { + rules[rule] = 'off'; + return rules; + }, {}), + })), + ); + }); }; diff --git a/packages/ruleset-migrator/src/transformers/extends.ts b/packages/ruleset-migrator/src/transformers/extends.ts index 2479f6cd0f..22fa9b93b8 100644 --- a/packages/ruleset-migrator/src/transformers/extends.ts +++ b/packages/ruleset-migrator/src/transformers/extends.ts @@ -22,28 +22,23 @@ async function processExtend( return ctx.tree.addImport(REPLACEMENTS[name], '@stoplight/spectral-rulesets'); } - const existingCwd = ctx.cwd; - const filepath = ctx.tree.resolveModule(name, existingCwd); + const filepath = ctx.tree.resolveModule(name, ctx.cwd); if (KNOWN_JS_EXTS.test(path.extname(filepath))) { return ctx.tree.addImport(`${path.basename(filepath, true)}_${path.extname(filepath)}`, filepath, true); } - const scope = ctx.tree.scope; - try { - ctx.cwd = path.dirname(filepath); - ctx.tree.scope = ctx.tree.scope.fork(); - return await process(await ctx.read(filepath, ctx.opts.fs, ctx.opts.fetch), ctx.hooks); - } finally { - ctx.tree.scope = scope; - ctx.cwd = existingCwd; - } + return await process(await ctx.read(filepath, ctx.opts.fs, ctx.opts.fetch), { + ...ctx, + cwd: path.dirname(filepath), + tree: ctx.tree.fork(), + }); } -const transformer: Transformer = function (ctx) { - ctx.hooks.add([ +const transformer: Transformer = function (registerHook) { + registerHook( /^(\/overrides\/\d+)?\/extends$/, - async (input): Promise => { + async (input, ctx): Promise => { const _extends = input as Ruleset['extends']; if (typeof _extends === 'string') { @@ -63,5 +58,5 @@ const transformer: Transformer = function (ctx) { return b.arrayExpression(extendedRulesets); }, - ]); + ); }; diff --git a/packages/ruleset-migrator/src/transformers/formats.ts b/packages/ruleset-migrator/src/transformers/formats.ts index 8d7d86ec92..6beae50c36 100644 --- a/packages/ruleset-migrator/src/transformers/formats.ts +++ b/packages/ruleset-migrator/src/transformers/formats.ts @@ -18,7 +18,7 @@ const REPLACEMENTS = Object.fromEntries( ]), ); -function transform(ctx: TransformerCtx, input: unknown): namedTypes.ArrayExpression { +function transform(input: unknown, ctx: TransformerCtx): namedTypes.ArrayExpression { assertArray(input); return b.arrayExpression( @@ -35,10 +35,8 @@ function transform(ctx: TransformerCtx, input: unknown): namedTypes.ArrayExpress export { transformer as default }; -const transformer: Transformer = function (ctx) { - const t = transform.bind(null, ctx); - - ctx.hooks.add([/^\/aliases\/[^/]+\/targets\/\d+\/formats$/, t]); - ctx.hooks.add([/^(\/overrides\/\d+)?\/formats$/, t]); - ctx.hooks.add([/^(\/overrides\/\d+)?\/rules\/[^/]+\/formats$/, t]); +const transformer: Transformer = function (registerHook) { + registerHook(/^\/aliases\/[^/]+\/targets\/\d+\/formats$/, transform); + registerHook(/^(\/overrides\/\d+)?\/formats$/, transform); + registerHook(/^(\/overrides\/\d+)?\/rules\/[^/]+\/formats$/, transform); }; diff --git a/packages/ruleset-migrator/src/transformers/functions.ts b/packages/ruleset-migrator/src/transformers/functions.ts index fecfdc6a88..93f9adc3c1 100644 --- a/packages/ruleset-migrator/src/transformers/functions.ts +++ b/packages/ruleset-migrator/src/transformers/functions.ts @@ -5,35 +5,32 @@ import { Ruleset } from '../validation/types'; export { transformer as default }; -const transformer: Transformer = function (ctx) { - ctx.hooks.add([ - /^$/, - (_ruleset): void => { - const ruleset = _ruleset as Ruleset; - const { functionsDir, functions } = ruleset; +const transformer: Transformer = function (registerHook) { + registerHook(/^$/, (_ruleset, ctx): void => { + const ruleset = _ruleset as Ruleset; + const { functionsDir, functions } = ruleset; - if (Array.isArray(functions) && functions.length > 0) { - ruleset.functions = functions.map(fn => - ctx.tree.resolveModule(`${fn}.js`, path.join(ctx.cwd, functionsDir ?? 'functions')), - ); - delete ruleset.functionsDir; - } - }, - ]); - - ctx.hooks.add([ - /^\/functions$/, - (value): null => { - assertArray(value); - - for (const fn of value) { + if (Array.isArray(functions) && functions.length > 0) { + for (const fn of functions) { assertString(fn); - const fnName = path.basename(fn, true); - const identifier = ctx.tree.addImport(fnName, fn, true); + const resolved = ctx.tree.resolveModule( + `${fn}.js`, + path.join(ctx.cwd, typeof functionsDir === 'string' ? functionsDir : 'functions'), + ); + const fnName = path.basename(resolved, true); + const identifier = ctx.tree.addImport(fnName, resolved, true); ctx.tree.scope.store(`function-${fnName}`, identifier.name); } + } + }); + + registerHook(/^\/functions$/, (value): null => { + assertArray(value); + return null; + }); - return null; - }, - ]); + registerHook(/^\/functionsDir$/, (value): null => { + assertString(value); + return null; + }); }; diff --git a/packages/ruleset-migrator/src/transformers/rules.ts b/packages/ruleset-migrator/src/transformers/rules.ts index cdb739205c..1e3282c1fd 100644 --- a/packages/ruleset-migrator/src/transformers/rules.ts +++ b/packages/ruleset-migrator/src/transformers/rules.ts @@ -53,40 +53,37 @@ function getDiagnosticSeverity(severity: DiagnosticSeverity | string): Diagnosti return Number.isNaN(Number(severity)) ? SEVERITY_MAP[severity] : Number(severity); } -const transformer: Transformer = function (ctx) { - ctx.hooks.add([ - /^$/, - (_ruleset): void => { - const ruleset = _ruleset as Ruleset; - if (ruleset.rules === void 0) return; - - const { rules } = ruleset; - - // this is to make sure order of rules is preserved after transformation - ruleset.rules = createOrderedLiteral(rules); - const order = Object.keys(rules); - - for (const [i, key] of order.entries()) { - if (!(key in REPLACEMENTS)) continue; - if (typeof rules[key] === 'object') continue; // we do not touch new definitions (aka custom rules). If one defines a rule like operation-2xx-response in their own ruleset, we shouldn't touch it. - const newName = REPLACEMENTS[key]; - if (newName in rules) { - rules[newName] = max(String(rules[key]), String(rules[newName])); - } else { - rules[newName] ??= rules[key]; - } - - order[i] = newName; - delete rules[key]; +const transformer: Transformer = function (registerHook) { + registerHook(/^$/, (_ruleset): void => { + const ruleset = _ruleset as Ruleset; + if (ruleset.rules === void 0) return; + + const { rules } = ruleset; + + // this is to make sure order of rules is preserved after transformation + ruleset.rules = createOrderedLiteral(rules); + const order = Object.keys(rules); + + for (const [i, key] of order.entries()) { + if (!(key in REPLACEMENTS)) continue; + if (typeof rules[key] === 'object') continue; // we do not touch new definitions (aka custom rules). If one defines a rule like operation-2xx-response in their own ruleset, we shouldn't touch it. + const newName = REPLACEMENTS[key]; + if (newName in rules) { + rules[newName] = max(String(rules[key]), String(rules[newName])); + } else { + rules[newName] ??= rules[key]; } - setOrder(rules, [...new Set([...order])]); - }, - ]); + order[i] = newName; + delete rules[key]; + } + + setOrder(rules, [...new Set([...order])]); + }); - ctx.hooks.add([ + registerHook( /^\/rules\/[^/]+\/then\/(?:[0-9]+\/)?function$/, - (value): namedTypes.Identifier | namedTypes.UnaryExpression => { + (value, ctx): namedTypes.Identifier | namedTypes.UnaryExpression => { assertString(value); if (KNOWN_FUNCTIONS.includes(value)) { @@ -96,5 +93,5 @@ const transformer: Transformer = function (ctx) { const alias = ctx.tree.scope.load(`function-${value}`); return alias !== void 0 ? b.identifier(alias) : b.unaryExpression('void', b.literal(0)); }, - ]); + ); }; diff --git a/packages/ruleset-migrator/src/tree/index.ts b/packages/ruleset-migrator/src/tree/index.ts index 3f8ac301b1..fddcd8ffb5 100644 --- a/packages/ruleset-migrator/src/tree/index.ts +++ b/packages/ruleset-migrator/src/tree/index.ts @@ -129,4 +129,22 @@ export class Tree { return resolved; } + + public fork(): Tree { + const scope = this.scope.fork(); + return new Proxy(this, { + get: (target, prop): Tree[keyof Tree] => { + if (prop === 'scope') { + return scope; + } + + const value = Reflect.get(target, prop, target) as Tree[keyof Tree]; + if (typeof value === 'function') { + return value.bind(target); + } + + return value; + }, + }); + } } diff --git a/packages/ruleset-migrator/src/types.ts b/packages/ruleset-migrator/src/types.ts index 326f777274..d26e8ff96a 100644 --- a/packages/ruleset-migrator/src/types.ts +++ b/packages/ruleset-migrator/src/types.ts @@ -26,10 +26,10 @@ export type MigrationOptions = { export type Hook = [ pattern: RegExp, - hook: (input: unknown) => Promise | ExpressionKind | null | void, + hook: (input: unknown, ctx: TransformerCtx) => Promise | ExpressionKind | null | void, ]; -export type Transformer = (ctx: TransformerCtx) => void; +export type Transformer = (registerHook: (...params: Hook) => void) => void; export type TransformerCtx = { readonly tree: Tree; diff --git a/yarn.lock b/yarn.lock index 5dbe7866e3..729d2b5994 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1935,7 +1935,7 @@ __metadata: languageName: node linkType: hard -"@stoplight/json@npm:3.17.0, @stoplight/json@npm:^3.17.0, @stoplight/json@npm:~3.17.1": +"@stoplight/json@npm:3.17.0, @stoplight/json@npm:^3.17.0, @stoplight/json@npm:~3.17.0, @stoplight/json@npm:~3.17.1": version: 3.17.0 resolution: "@stoplight/json@npm:3.17.0" dependencies: @@ -2129,7 +2129,7 @@ __metadata: version: 0.0.0-use.local resolution: "@stoplight/spectral-ruleset-migrator@workspace:packages/ruleset-migrator" dependencies: - "@stoplight/json": 3.17.0 + "@stoplight/json": ~3.17.0 "@stoplight/ordered-object-literal": 1.0.2 "@stoplight/path": 1.3.2 "@stoplight/spectral-core": ^1.1.0