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

JS Public API Update #12722

Merged
merged 10 commits into from
Nov 14, 2024
Merged

JS Public API Update #12722

merged 10 commits into from
Nov 14, 2024

Conversation

rtibbles
Copy link
Member

@rtibbles rtibbles commented Oct 17, 2024

N.B. I strongly advise you to only review this commit by commit (skipping the very large commits that move all files and update all imports) - opening the Files changed tab for all the changes here will probably crash your tab, if not your browser.

Summary

For the new API this implements, see #5488

  • Creates migration script that reads file imports in source code and maps them to the updated import paths - available as kolibri-tools command migrate

    • Shows a warning to a user if a mapped import maps to null
    • Shows a warning for jest.mocks where the module mapping is more complex, as the mocks will need to be resolved manually
  • Migrates every APIResource into its own module, all except TaskResource, is in kolibri-common

  • Migrate the new independent packages into their own packages in the kolibri packages directory

  • Creates a kolibri package

    • migrates all public modules there
    • sets exports explicitly in the package.json to prevent access to internal only modules
    • adds an 'exposes' field that details the external modules the core JS API also supports
  • Migrates all other common modules to kolibri-common

  • Removes all core aliases

    • Removes the use of the custom import resolver in our linting configuration, but retains it for use in a couple of other cases for now
  • Updates core externals to map to a spec object defined in the new kolibri package

  • Updates the spec export tools to introspect the kolibri package package.json to generate the apiSpec file

    • Use require statements exclusively to ensure we are always exposing the full module object
    • Add a special module resolution for the vue composition API plugin that managed to break this due to its janky internal module configuration

References

Fixes #5488

Reviewer guidance

The most changes here are in two commits, one that moves all the files to their new locations, and the other that updates all the import references. I would strongly recommend not viewing these commits in a browser interface.

Aside from this, absolutely nothing should have changed from a functional point of view, and all tests should be passing. If anything is awry, then that will need fixing.

There may be minor deviations from the spec, as it became a little tiresome to keep them in sync, please flag if anything looks suss, and I can either explain or rectify. Also happy to hear comments where the spec doesn't make as much sense as it might!

Lastly, this is not a finalization of the api spec - I suspect that we can finish removing the core vuex, and hence remove it from the core API completely, and if we want to break up, say, commonCoreStrings into more explicit translator modules, that also seems like a good idea.

Two other open questions I had were about the kolibri package:

  1. For all the other new packages I created, I put everything inside a src directory inside the package, I am not sure why I didn't do the same for the kolibri package, but it would be straight forward enough to do and then update the exports field accordingly.
  2. Is it obvious enough which things are private and which are public in this package? i.e. by using the exports field, or would it be better to try to make it more obvious by either prefixing private modules with _, or putting the api files into a special folder, and the private ones elsewhere?

Testing checklist

  • Contributor has fully tested the PR manually
  • If there are any front-end changes, before/after screenshots are included
  • Critical user journeys are covered by Gherkin stories
  • Critical and brittle code paths are covered by unit tests

PR process

  • PR has the correct target branch and milestone
  • PR has 'needs review' or 'work-in-progress' label
  • If PR is ready for review, a reviewer has been added. (Don't use 'Assignees')
  • If this is an important user-facing change, PR or related issue has a 'changelog' label
  • If this includes an internal dependency change, a link to the diff is provided

Reviewer checklist

  • PR is fully functional
  • PR has been tested for accessibility regressions
  • External dependency files were updated if necessary (yarn and pip)
  • Documentation is updated
  • Contributor is in AUTHORS.md

@github-actions github-actions bot added DEV: renderers HTML5 apps, videos, exercises, etc. APP: Device Re: Device App (content import/export, facility-syncing, user permissions, etc.) APP: Facility Re: Facility App (user/class management, facility settings, csv import/export, etc.) APP: Learn Re: Learn App (content, quizzes, lessons, etc.) APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) APP: User Re: User app (sign-in, sign-up, user profile, etc.) APP: Setup Wizard Re: Setup Wizard (facility import, superuser creation, settings, etc.) DEV: frontend DEV: tools Internal tooling for development labels Oct 17, 2024
@rtibbles
Copy link
Member Author

Switching to draft - something has gone awry in the build that I hadn't seen previously.

@rtibbles rtibbles marked this pull request as draft October 17, 2024 23:46
@rtibbles rtibbles marked this pull request as ready for review October 18, 2024 01:03
@rtibbles
Copy link
Member Author

Issues resolved.

Copy link
Member

@marcellamaki marcellamaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only added a couple of comments so far and am still digesting. I haven't decided yet about my opinion on

i.e. by using the exports field, or would it be better to try to make it more obvious by either prefixing private modules with _, or putting the api files into a special folder, and the private ones elsewhere?

and will come back to that later with some thoughts.

Not blocking for this PR but I think it would be very worthwhile to update the dev docs both for accuracy and also to have some guidance for common vs. core, and in what circumstances we will make updates to the core API spec (at least as we move towards/before we get to v1.x)

"vuex": "^3.6.2",
"vuex-router-sync": "^5.0.0",
"xstate": "^4.38.3"
"core-js": "^3.38"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what's happening in this commit generally "Add new base kolibri packages" is that for everything that got moved out of kolibri core, where the dependencies were required in a moved file, the package.json for that is in the plugin where the components that require those dependencies are located? Or the packages, or kolibri-common.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - now this package.json only includes things that are only imported in the default frontend bundle (which is very little). Most of the imports are used either in the kolibri package or the kolibri-common package, and so are now specified there.

// conditionally imports the prod or dev version of the package based on the NODE_ENV, but
// only exposes the common JS builds, and reassigns them to the module.exports, thereby
// preventing webpack from importing it consistently in both how we use it internally
// in the core bundle, and also then access it via externals in the plugin bundles.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am following this comment 80% and I think I understand in broad strokes what is happening, but if I were to really explain the why, I don't think I would get it. The two phrases that I keep getting tripped up on are

only exposes the common JS builds, and reassigns them to the module.exports

and

also then access it via externals in the plugin bundles

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, internally, the composition api does something really janky for its exports, which breaks how we expose the 'core' modules on the default frontend bundle for the plugins to reuse without having to bundle them. This is a workaround for that.

If it makes you feel any better, once we've upgraded to Vue 2.7, we can get rid of this entirely, because we won't need the plugin any more!

packages/kolibri/apiSpec.js Outdated Show resolved Hide resolved
@rtibbles rtibbles force-pushed the public_api branch 5 times, most recently from fceba45 to 5b50afb Compare October 25, 2024 21:41
@bjester
Copy link
Member

bjester commented Oct 31, 2024

On the topic of public vs private, the exports field, and prefixing _ to explicitly distinguish private code, could using the _ convention allow us to write a linting rule that ensures the exports field is kept up-to-date?

@marcellamaki
Copy link
Member

the public files will end up having to do a lot of long relative path imports, and code that is used together doesn't naturally live together

Right, I hadn't really thought about that. I think prefixing is fine then. And I will reread the documentation updates, and see if there's anything else that I can think of that might be worth adding there to help.

@bjester
Copy link
Member

bjester commented Oct 31, 2024

Re: public vs private and exports

It appears this feature is also possible, according to node.js docs, where you specify null to identify what's internal.

{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
} 

@bjester
Copy link
Member

bjester commented Oct 31, 2024

For all the other new packages I created, I put everything inside a src directory inside the package, I am not sure why I didn't do the same for the kolibri package, but it would be straight forward enough to do and then update the exports field accordingly.

Consistency always feels good and helps to clearly distinguish from package tooling or other settings, from the actual source code, so I support this

@rtibbles
Copy link
Member Author

On the topic of public vs private, the exports field, and prefixing _ to explicitly distinguish private code, could using the _ convention allow us to write a linting rule that ensures the exports field is kept up-to-date?

Yes, I think so - and we could even have the exports field auto generated similarly to the way that the apiSpec file is autogenerated from the exports and exposes fields.

@rtibbles
Copy link
Member Author

It appears this feature is also possible

Interesting, so technically you could do a wild card instead of feature and have it so any folder labelled with internal is not exportable.

import useUser, { useUserMock } from 'kolibri.coreVue.composables.useUser';
import useSnackbar, { useSnackbarMock } from 'kolibri.coreVue.composables.useSnackbar';
import useUser, { useUserMock } from 'kolibri/composables/useUser'; // eslint-disable-line
import useSnackbar, { useSnackbarMock } from 'kolibri/composables/useSnackbar'; // eslint-disable-line
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does eslint not like these lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the named exports only exist inside the mock, not inside the composable module itself, but because it is being mocked, they are available as an export.

@@ -3,6 +3,10 @@
"description": "Learn Plugin for Kolibri",
"private": true,
"version": "0.0.1",
"dependencies": {
"deep-object-diff": "^1.1.9",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to your work here, but I'm curious if lodash can accomplish what this dependency does

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, looking at the usage in place, it is only being used to check deep equality, so lodash's isEqual would do the job here instead.

Copy link
Member

@bjester bjester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spot checked a bunch of files but didn't find any concerns other than those I've already brought up before this. 👍

@rtibbles rtibbles force-pushed the public_api branch 2 times, most recently from e2c890f to 687990c Compare November 1, 2024 22:39
@rtibbles
Copy link
Member Author

rtibbles commented Nov 1, 2024

Added follow up issue for resolving the src folder work here: #12780

@rtibbles rtibbles merged commit 143478a into learningequality:develop Nov 14, 2024
35 checks passed
@rtibbles rtibbles deleted the public_api branch November 14, 2024 23:04
@indirectlylit
Copy link
Contributor

💥 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
APP: Coach Re: Coach App (lessons, quizzes, groups, reports, etc.) APP: Device Re: Device App (content import/export, facility-syncing, user permissions, etc.) APP: Facility Re: Facility App (user/class management, facility settings, csv import/export, etc.) APP: Learn Re: Learn App (content, quizzes, lessons, etc.) APP: Setup Wizard Re: Setup Wizard (facility import, superuser creation, settings, etc.) APP: User Re: User app (sign-in, sign-up, user profile, etc.) DEV: Core JS API Changes related to, or to the Core JS API DEV: frontend DEV: renderers HTML5 apps, videos, exercises, etc. DEV: tools Internal tooling for development
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Separate frontend core API into 'public' and 'internally shared'
4 participants