Skip to content

Commit

Permalink
fix(ruleset-migrator): resolve npm packages correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip committed Jan 14, 2022
1 parent 4a63af9 commit 65b0a47
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 16 deletions.
3 changes: 2 additions & 1 deletion packages/ruleset-migrator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"ast-types": "0.14.2",
"astring": "^1.7.5",
"reserved": "0.1.2",
"tslib": "^2.3.1"
"tslib": "^2.3.1",
"validate-npm-package-name": "3.0.0"
},
"devDependencies": {
"@stoplight/spectral-core": "^1.1.0",
Expand Down
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extends:
- some-npm-ruleset-i-will-have-to-add-somehow
29 changes: 26 additions & 3 deletions packages/ruleset-migrator/src/__tests__/ruleset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as fetchMock from 'fetch-mock';

import { migrateRuleset } from '..';
import * as fixtures from './__fixtures__/.cache/index.json';
import { serveAssets } from '../../../../test-utils/node';

const cwd = '/.tmp/spectral';

Expand Down Expand Up @@ -172,10 +173,23 @@ describe('migrator', () => {

describe('custom npm registry', () => {
it('should be supported', async () => {
serveAssets({
'https://unpkg.com/custom-npm-ruleset': {
functions: ['test'],
rules: {
rule2: {
then: {
given: '$',
function: 'test',
},
},
},
},
});
await fs.promises.writeFile(
path.join(cwd, 'custom-npm-provider.json'),
JSON.stringify({
extends: 'spectral:asyncapi',
extends: ['custom-npm-ruleset'],
formats: ['oas2'],
rules: {
rule: {
Expand All @@ -195,9 +209,18 @@ describe('migrator', () => {
}),
).toEqual(`import {oas2} from "https://unpkg.com/@stoplight/spectral-formats";
import {truthy} from "https://unpkg.com/@stoplight/spectral-functions";
import {asyncapi} from "https://unpkg.com/@stoplight/spectral-rulesets";
import test from "https://unpkg.com/custom-npm-ruleset/functions/test.js";
export default {
"extends": asyncapi,
"extends": [{
"rules": {
"rule2": {
"then": {
"given": "$",
"function": test
}
}
}
}],
"formats": [oas2],
"rules": {
"rule": {
Expand Down
2 changes: 2 additions & 0 deletions packages/ruleset-migrator/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ export async function migrateRuleset(filepath: string, opts: MigrationOptions):
const hooks = new Set<Hook>();
const ctx: TransformerCtx = {
cwd,
filepath,
tree,
opts: {
fetch,
...opts,
},
npmRegistry: npmRegistry ?? null,
hooks,
read,
};
Expand Down
4 changes: 2 additions & 2 deletions packages/ruleset-migrator/src/transformers/extends.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ async function processExtend(
return ctx.tree.addImport(REPLACEMENTS[name], '@stoplight/spectral-rulesets');
}

const filepath = ctx.tree.resolveModule(name, ctx.cwd);
const filepath = ctx.tree.resolveModule(name, ctx, 'ruleset');

if (KNOWN_JS_EXTS.test(path.extname(filepath))) {
return ctx.tree.addImport(`${path.basename(filepath, true)}_${path.extname(filepath)}`, filepath, true);
}

return await process(await ctx.read(filepath, ctx.opts.fs, ctx.opts.fetch), {
...ctx,
cwd: path.dirname(filepath),
filepath,
tree: ctx.tree.fork(),
});
}
Expand Down
5 changes: 3 additions & 2 deletions packages/ruleset-migrator/src/transformers/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ const transformer: Transformer = function (hooks) {
for (const fn of functions) {
assertString(fn);
const resolved = ctx.tree.resolveModule(
`${fn}.js`,
path.join(ctx.cwd, typeof functionsDir === 'string' ? functionsDir : 'functions'),
path.join('./', typeof functionsDir === 'string' ? functionsDir : 'functions', `./${fn}.js`),
ctx,
'function',
);
const fnName = path.basename(resolved, true);
const identifier = ctx.tree.addImport(fnName, resolved, true);
Expand Down
26 changes: 19 additions & 7 deletions packages/ruleset-migrator/src/tree/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { namedTypes, builders as b } from 'ast-types';
import * as path from '@stoplight/path';
import * as astring from 'astring';
import { MigrationOptions } from '../types';
import { MigrationOptions, TransformerCtx } from '../types';
import { commonjs } from './commonjs';
import { esm } from './esm';
import { IModule } from './types';
import requireResolve from '../requireResolve';
import { Scope } from './scope';
import { isPackageImport } from '../utils/isPackageImport';

export { Scope };

Expand All @@ -31,7 +32,7 @@ export class Tree {

readonly #npmRegistry;
readonly #module: IModule;
readonly #localPaths = new Set<string>();
readonly #resolvedPaths = new Set<string>();

public ruleset?: namedTypes.ObjectExpression;
public scope: Scope;
Expand Down Expand Up @@ -82,7 +83,7 @@ export class Tree {
.sort(sortImports)
.flatMap(([source, identifiers]) => {
const resolvedSource =
this.#npmRegistry !== null && !this.#localPaths.has(source)
this.#npmRegistry !== null && !this.#resolvedPaths.has(source) && !source.startsWith(this.#npmRegistry)
? path.join(this.#npmRegistry, source)
: source;

Expand Down Expand Up @@ -121,10 +122,21 @@ export class Tree {
return b.identifier(uniqName);
}

public resolveModule(identifier: string, cwd: string): string {
const resolved = path.isURL(identifier) ? identifier : requireResolve?.(identifier) ?? path.join(cwd, identifier);
if (resolved.startsWith(cwd)) {
this.#localPaths.add(resolved);
public resolveModule(identifier: string, ctx: TransformerCtx, kind: 'function' | 'ruleset'): string {
let resolved: string;
if (path.isURL(identifier) || path.isAbsolute(identifier)) {
resolved = identifier;
this.#resolvedPaths.add(identifier);
} else if (kind === 'ruleset' && isPackageImport(identifier)) {
resolved =
ctx.npmRegistry !== null
? path.join(ctx.npmRegistry, identifier)
: requireResolve?.(identifier, { paths: [ctx.cwd] }) ?? path.join(ctx.cwd, identifier);
} else if (ctx.npmRegistry !== null && ctx.filepath.startsWith(ctx.npmRegistry)) {
resolved = path.join(ctx.filepath, identifier);
} else {
resolved = path.join(ctx.filepath, '..', identifier);
this.#resolvedPaths.add(resolved);
}

return resolved;
Expand Down
4 changes: 3 additions & 1 deletion packages/ruleset-migrator/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type TransformerCtx = {
fetch: Fetch;
};
readonly hooks: Set<Hook>;
cwd: string;
readonly cwd: string;
readonly filepath: string;
readonly npmRegistry: string | null;
read(filepath: string, fs: MigrationOptions['fs'], fetch: Fetch): Promise<Ruleset>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { isPackageImport } from '../isPackageImport';

describe('isPackageImport util', () => {
it.each([
'nimma/legacy',
'nimma',
'lodash',
'lodash/get',
'lodash/get.js',
'@stoplight/path',
'@stoplight/spectral-core',
'@stoplight/spectral-core/dist/file.js',
])('given valid %s package import, should return true', input => {
expect(isPackageImport(input)).toBe(true);
});

it.each(['', '/nimma/legacy', 'path', 'https://cdn.skypack.dev/@stoplight/spectral-core'])(
'given invalid %s import, should return false',
input => {
expect(isPackageImport(input)).toBe(false);
},
);
});
12 changes: 12 additions & 0 deletions packages/ruleset-migrator/src/utils/isPackageImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as validate from 'validate-npm-package-name';

const isValidPackageName = (packageName: string): boolean => validate(packageName).validForNewPackages;

export const isPackageImport = (packageName: string): boolean => {
const fragments = packageName.split('/');
if (packageName.startsWith('@') && fragments.length >= 2) {
fragments.splice(0, 2, `${fragments[0]}/${fragments[1]}`);
}

return fragments.every(isValidPackageName);
};
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2148,6 +2148,7 @@ __metadata:
prettier: ^2.4.1
reserved: 0.1.2
tslib: ^2.3.1
validate-npm-package-name: 3.0.0
languageName: unknown
linkType: soft

Expand Down

0 comments on commit 65b0a47

Please sign in to comment.