Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Javascript function to load custom ruleset from file #1956

Closed
ivangsa opened this issue Nov 15, 2021 · 33 comments · Fixed by #1988
Closed

Expose Javascript function to load custom ruleset from file #1956

ivangsa opened this issue Nov 15, 2021 · 33 comments · Fixed by #1988
Assignees
Labels
enhancement New feature or request released

Comments

@ivangsa
Copy link

ivangsa commented Nov 15, 2021

User story.
As a developer consuming spectral-core from javascript, I can load custom ruselet from yml files, so that I can use them programatically on a microservice.

Is your feature request related to a problem?
I'm trying to use spectral-core 6 from javascript code and I'm not able to programatically load rulesets from yml files.
Loading files as yml files doesn't work as it doesn't resolve functions (like truthy, falsy...) and all I get are string properties...

Digging in spectral-core source code there is a function for reading files is included in spectral-cli but that function is not exported as a type.d.ts (or is it?).

https://github.com/stoplightio/spectral/blob/develop/packages/cli/src/services/linter/utils/getRuleset.ts#L36

Trying to import the function like this doesn't work:

import { getRuleset } from '@stoplight/spectral-cli/dist/services/linter/utils';

(I guess I can not import from dist folder just like that)

Describe the solution you'd like

I would like spectral-core to expose getRuleset function for other applications to use it programatically
or even update Spectral.setRuleset signature so it can receive rules as string urls

Spectral.setRuleset(ruleset: RulesetDefinition | Ruleset | string): void
const fileUrl = '<local or http file url>';
const spectral = new Spectral();
spectral.setRuleset(fileUrl);
spectral.run(..);

Additional context
Add any other context or screenshots about the feature request here.

@Andras-Csanyi
Copy link

I did struggle with this earlier today, and here is my conclusion (frustration):

  • you need to include spectral-cli in order to be able to use that method (I don't like this fact neither)
  • it uses migrateRuleset which returns a js file which should be bundled and loaded, see the load method at the bottom of the same file

When I included the this file (getRuleset) it failed and the error was that it couldn't load node presets from here: https://github.com/stoplightio/spectral/tree/develop/packages/ruleset-bundler/src/presets. So even if you just include the package and include the file you'll see error, meaning you can't use it.

The solution was that I implemented (almost copy-paste from here) my own solution in the codebase I work with.

@P0lip
Copy link
Contributor

P0lip commented Nov 16, 2021

👋
setRuleset is meant to work with the JS-ish ruleset.
Overall, the JS ruleset is a much better fit if you mean to use Spectral programmatically, as it greatly reduces the debugging pain as well as may give you type-safety if you happen to use TS.
Having said that, I'm totally open for exposing that function somewhere for a public consumption - probably in ruleset-bundler package, but it's to be decided.

@ivangsa
Copy link
Author

ivangsa commented Nov 16, 2021

Hi @P0lip

we would like to keep the rules in yml files separated from microservice code, one of the reasons it's because we want to also load same rules in the vscode extension and/or from the command line (depending on users)

Spectral.setRulesetFile(uri) would work as a great place for this functionality, but ofcourse please analyse the best module for it..

we have a Pull Request to your official vscode plugin to upgrade it to Spectral6 and loading rules form .spectral[.json|.yml|.js] is the last missing piece

Regarding the vscode extension, we have made a very simple implementation/refactoring... it's under 250 loc + tests, and includes all functionality from current extension but upgraded to Spectral6 with a very simple build process

We see that vscode extension has not been updated in a while.
If you would like to review our contribution We can review it in the original issue if you like:
stoplightio/vscode-spectral#107

@P0lip
Copy link
Contributor

P0lip commented Nov 17, 2021

@ivangsa
Thanks for the contribution to vscode-spectral - really appreciated!

The function is unlikely to become a method of the Spectral class. It'd probably be in ruleset-bundler, and be consumed in the following way

import { Spectral } from '@stoplight/spectral-core';
import { importLegacyRuleset } from '@stoplight/ruleset-bundler';
import * as fs from 'fs';

const s = new Spectral();
s.setRuleset(await importLegacyRuleset('.spectral.yaml'), { fs }));

The actual implementation will be presumably slightly different.

@P0lip
Copy link
Contributor

P0lip commented Nov 23, 2021

Hey, I've started working on it.
I'm not sure when exactly this would be delivered, but hopefully either this week or the next one

@P0lip P0lip self-assigned this Dec 2, 2021
@P0lip P0lip added the enhancement New feature or request label Dec 2, 2021
@P0lip
Copy link
Contributor

P0lip commented Dec 6, 2021

@Andras-Csanyi @ivangsa
Here's a draft PR #1988.
In short, this is how the procedure would look like:

// ...

import { bundleAndLoadRuleset } from "@stoplight/spectral-ruleset-bundler/with-loader";

// ...

const rulesetFile = path.join(__dirname, ".spectral.yaml");
spectral.setRuleset(await bundleAndLoadRuleset(rulesetFile, { fs, fetch }));

What do you folks think?

@ivangsa
Copy link
Author

ivangsa commented Dec 7, 2021

@P0lip it looks perfect!!
when it's ready I will finish the migration to spectal6 of vscode extension..
and this will reduce the code even further, the whole plugin could be just under 200 loc..

@stoplight-bot
Copy link
Collaborator

🎉 This issue has been resolved in version @stoplight/spectral-ruleset-bundler-v1.1.0 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

@stoplight-bot
Copy link
Collaborator

🎉 This issue has been resolved in version @stoplight/spectral-ruleset-migrator-v1.6.0 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

@P0lip
Copy link
Contributor

P0lip commented Dec 15, 2021

@ivangsa it's out, LMK how it goes and if there's anything I could help you with.

@ivangsa
Copy link
Author

ivangsa commented Dec 16, 2021

Hi @P0lip, thanks for the update

I'm not sure what I'm doing wrong but I can not import the module:

I added to my package.json:

"@stoplight/spectral-ruleset-bundler": "^1.1.0",

I see that the package is in node_modules, and that has that "export" in its package.json but it fails importing it (both vscode and webpack) And I can import other functions in ruleset-bundler package :-(

image

@P0lip
Copy link
Contributor

P0lip commented Dec 16, 2021

Hey!
It's probably because of your version of TypeScript.
package exports is supported as of TS 4.5.
Here's a related issue in their repo microsoft/TypeScript#33079.


Although having a peek at some of the most recent comments, it seems like we may need to add types to make TS happy.

If that's indeed the case, perhaps you can try setting the path for now

 "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@stoplight/spectral-ruleset-bundler/with-loader": ["packages/ruleset-bundler/src/loader/node.ts"]
    }
}

while I'll address the issue with the lack of types.

@ivangsa
Copy link
Author

ivangsa commented Dec 16, 2021

I was just trying with the whole path

import { bundleAndLoadRuleset } from '@stoplight/spectral-ruleset-bundler/dist/loader/node';

let's see if it works

@ivangsa
Copy link
Author

ivangsa commented Dec 16, 2021

  • no luck, with "typescript": "^4.5.4",
  • nor using paths in tsconfig
  • nor with the absolute path (is not exported)
  • and editing package.json in node_modules to add types (just to try) it doesnt seem to take effect
    :-(

@P0lip
Copy link
Contributor

P0lip commented Dec 16, 2021

@ivangsa do you have a repo somewhere I could try it out on?

@ivangsa
Copy link
Author

ivangsa commented Dec 16, 2021

@jamietanna
Copy link
Contributor

I'm also seeing:

    Cannot find module '@stoplight/spectral-ruleset-bundler/with-loader' from 'test/testharness.js'

When using CommonJS with Node v17.3.0

Amending it as follows seems to work:

-const { bundleAndLoadRuleset } = require("@stoplight/spectral-ruleset-bundler/with-loader");
+const { bundleAndLoadRuleset } = require("@stoplight/spectral-ruleset-bundler/dist/loader/node");

@jamietanna
Copy link
Contributor

jamietanna commented Dec 22, 2021

Edit: Deleted message, as it was due to me passing the contents of the file to bundleAndLoadRuleset instead of the path to the file

@P0lip
Copy link
Contributor

P0lip commented Dec 28, 2021

Hey folks!
I'll try to respond later this week.
It's on my radar, but with the Holidays season and some days off, I'm lagging behind a bit.
Thank you for your understanding!

@ivangsa
Copy link
Author

ivangsa commented Dec 29, 2021

(no worries on this side... I'll be offline for a couple of weeks)

@ivangsa
Copy link
Author

ivangsa commented Jan 11, 2022

hi @P0lip, any news on this?

1 similar comment
@ivangsa
Copy link
Author

ivangsa commented Jan 17, 2022

hi @P0lip, any news on this?

@mima0815
Copy link

We are the same issue here with TypeScript:

import { Spectral } from '@stoplight/spectral-core';
import { fetch } from '@stoplight/spectral-runtime';
import { bundleAndLoadRuleset } from '@stoplight/spectral-ruleset-bundler/dist/loader/node';
import * as fs from 'fs'

const spectral = new Spectral();
const rulesetFilepath = path.join(__dirname, ".spectral.yaml");
spectral.setRuleset(await bundleAndLoadRuleset(rulesetFilepath, { fs, fetch }));

this throws then:

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './dist/loader/node' is not defined by "exports" in /development/testarrow/node_modules/@stoplight/spectral-ruleset-bundler/package.json
    at new NodeError (internal/errors.js:322:7)
    at throwExportsNotFound (internal/modules/esm/resolve.js:332:9)
    at packageExportsResolve (internal/modules/esm/resolve.js:565:3)
    at resolveExports (internal/modules/cjs/loader.js:450:36)
    at Function.Module._findPath (internal/modules/cjs/loader.js:490:31)
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:888:27)
    at Function.Module._load (internal/modules/cjs/loader.js:746:27)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:101:18)
    at Object.<anonymous> (/development/testarrow/packages/testarrow/src/server/server.ts:9:1)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:101:18)

exporting the functions properly should help all TS users

@Qbaloz
Copy link

Qbaloz commented May 17, 2022

hi @P0lip, any news on this?

@burk3rt
Copy link

burk3rt commented Jul 13, 2022

hi @P0lip, are there any finding? I'm having the same issue.

@benjamin-mogensen
Copy link

benjamin-mogensen commented Jul 20, 2022

I am having a problem with require of the ruleset bundler.

It only happens after the app is built for windows with electron builder. It then complains that it cannot find the file location of "with-loader". Almost like it cannot navigate to that path on windows in a packaged app.

It works fine if I run the app, that uses the ruleset bundler, from console, ie start my electron app directly.

It works fine after my app is build for Mac.

Do you have any workaround? I was thinking to go around the "with-loader" and not require that directly but I can't figure out how to use the bundle function directly that is being called from "with-loader" exported function.

@tillig
Copy link
Contributor

tillig commented Mar 8, 2023

Man, this hit me, too. This with-loader thing just does not work. I'm going to have to probably just copy/paste my own copy of this thing into my project to get me past it; the docs are definitely not here for this.

@benjamin-mogensen
Copy link

@tillig I solved my problem by building the app on the specific environments, here is my "guide" I wrote down:

Manual build process

Mac x64 system can build Mac x64 and Windows x64 packaged application
Mac ARM system must be used to build for ARM based Mac
Make sure to run as sudo when building on Mac

Background

Building the Windows packaged application on a Mac ARM system will result in @stoplight/spectral-ruleset-bundler/with-loader require error thrown on application start after install
The Mac x64 build, if build on an ARM based Mac, will also result in @stoplight/spectral-ruleset-bundler/with-loader require error thrown on application start after install

Not sure if it solves your issue, but maybe give it a try.

I also found that building on GitHub on macos-latest, seems to be able to build fine for MacOS ARM, MacOS x64 and Win x64. Not sure how it works in the depth. But so far, fingers crossed, the builds seems to work also with the Spectral validations.

@tillig
Copy link
Contributor

tillig commented Mar 8, 2023

My use case is that I have a set of Mocha tests (with ts-node) in Typescript that I'm using to test my custom ruleset. I'm not building an app, basically just running Mocha. I tried to update to the latest Spectral and hit this issue.

@kristens-work
Copy link

I am also experiencing the exact same issue. My use case is similar to @tillig in that I want to load the file based ruleset to execute some tests against each rule.

br-tyler-milner added a commit to br-tyler-milner/spectral-tests-example that referenced this issue Apr 28, 2023
…er/with-loader' or its corresponding type declarations" error by specifying path to loader directly based on stoplightio/spectral#1956 (comment).
@br-tyler-milner
Copy link

I was running into this cannot find module '@stoplight/spectral-ruleset-bundler/with-loader' or its corresponding type declarations error when trying to implement unit testing using TypeScript + Jest for some of my custom Spectral rules. Replacing the with-loader path component with /dist/loader/node in the import statement as suggested by some didn't make a difference for me.

The fix in my case was to add a tsconfig.json file to my repository with the following contents:

{
    "compilerOptions": {
        "moduleResolution": "node16"
    }
}

After adding the tsconfig.json, I was able to npm test with no more issues related to @stoplight/spectral-ruleset-bundler/with-loader! Sample project available here.

@travisgosselin
Copy link

Ran into this same issue - @br-tyler-milner - your suggestion worked for me. It makes sense in my case to modify the module resolution anyhow https://www.typescriptlang.org/tsconfig#moduleResolution

@fmazeiras
Copy link

to fix the issue of find module '@stoplight/spectral-ruleset-bundler/with-loader i change tsconfig.json xto
{
"compilerOptions": {
"moduleResolution": "node16"
}
}

but when the spectral rule use a custom function i have the following error "Error: 'default' is not exported by "

image

here is the pagination.js file
image

did you have any ideas?

Regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request released
Projects
None yet
Development

Successfully merging a pull request may close this issue.