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

Typing of ResourceLoader modules other than mediawiki.base #35

Open
Derugon opened this issue Jan 22, 2024 · 2 comments
Open

Typing of ResourceLoader modules other than mediawiki.base #35

Derugon opened this issue Jan 22, 2024 · 2 comments

Comments

@Derugon
Copy link
Collaborator

Derugon commented Jan 22, 2024

Motivation

The MediaWiki API contains various modules managed by the ResourceLoader (see Resources.php). Some ResourceLoader modules include code whose associated types are in several .d.ts files. For example, type declarations of scripts from mediawiki.base can be found in:

  • global.d.ts,
  • html.d.ts,
  • index.d.ts,
  • log.d.ts,
  • and others…

Some d.ts files also contain types for code loaded by different modules. For example, language.d.ts contains type declarations for:

  • mediawiki.language,
  • mediawiki.language.months,
  • mediawiki.language.names, and
  • mediawiki.language.specialCharacters.

In general, most of MediaWiki and jQuery core modules managed by the ResourceLoader are not enabled directly when a page is loaded. By using import "types-mediawiki" in another package, all available type declarations are imported.

In some cases, I would like to import only the modules I know are active on a page (or modules I'm going to activate dynamically, e.g. with mw.loader.using(...)). To do this, I could import the corresponding files (import "types-mediawiki/mw/Api"), but there are a few issues with this:

  • Importing a module may require importing several files,
  • .d.ts file names don't always match ResourceLoader module names.

Proposal part 1

I suggest to change the declaration file hierarchy so each ResourceLoader module can be imported individually, e.g.:

import "types-mediawiki/jquery.tablesorter";
import "types-mediawiki/mediawiki.api";
import "types-mediawiki/mediawiki.htmlform";

Below are a 3 proposed implementations for this.

Alternative A

Synchronize files with MediaWiki core: create one .d.ts file per .js source code file, then export one module per ResourceLoader module, maybe directly in index.d.ts, e.g.:

declare module "mediawiki.diff" {
	// dependencies
	import "oojs-ui";
	import "./mediawiki.api";

	// scripts
	import "./mediawiki.diff/diff";
	import "./mediawiki.diff/inlineFormatToggle";
}

(note: to help with synchronization, this list can be generated automatically from the source code)

Alternative B

Create one TS module per ResourceLoader module: gather all .d.ts files of a single ResourceLoader module in a directory, one directory per ResourceLoader module.

Alternative C

Create one .d.ts file per ResourceLoader module, containing type declarations for all scripts imported by that ResourceLoader module.

Proposal part 2

Currently, when using import "types-mediawiki", all available modules are imported.

Assuming it is possible to choose which modules to import (if desired), the base module (when using import "types-mediawiki";) may only export a subset of available modules, e.g.:

  1. Export the most common frequently used modules,
  2. Export all modules that are active when a page is loaded on Wikipedia or WMF wikis.
@Derugon
Copy link
Collaborator Author

Derugon commented Feb 13, 2024

You will find an initial implementation in this branch: https://github.com/Derugon/types-mediawiki/tree/split (based on the #36 MR branch, diff comparison here).

(note that none of the missing modules have been added to this branch, so some modules are empty (for now): they have only been added to validate other module dependencies)

Alternative B is used here: one folder per module. What I didn't thought about is that unlike alternatives A and C, this alternative allows .../node_modules/types-mediawiki to be treated as a type root,

The organisation of files within a module is similar to the one currently used (roughly one file per namespace).

@Derugon
Copy link
Collaborator Author

Derugon commented Mar 1, 2024

After a few failed and successful attempts at implementing the previously mentioned alternatives:

Using declare module { ... } seems to properly extend existing modules, but I'm not familiar enough with this syntax to find a way to declare new modules without creating real files and folders.

Another issue I didn't consider is that all global declarations are always visible, if the files they come from have been scanned by the TS compiler. The recommended approach in the README is to use "include": ["./node_modules/types-mediawiki"] so all declaration files are included, and also all globals are always visible. The only way I was able to find to hide global declarations is to prevent the complier from scanning declaration files (which also disables sub-module autocompletion in import). This way we could instead recommend the use of typeRoots:

"compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./node_modules/types-mediawiki"]
}

Then, one may only enable a few MediaWiki modules by specifying types:

"compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./node_modules/types-mediawiki"],
    "types": ["mediawiki.base", "mediawiki.ForeignApi", "mediawiki.language"]
}

This approach also requires having one folder per sub-module, because single declaration files (e.g. mediawiki.ForeignApi.d.ts module file in root library folder) do not seem to be scanned properly. We would then have:

  • One folder for base MediaWiki declarations, that do not require any sub-module (all declarations in resources/src/startup/).
  • One folder per sub-module (i.e. mediawiki.api, mediawiki.base, mediawiki.cldr, etc.).

Below is an implementation plan of this approach. at the very least I'm going to wait for #36 to be closed (whether merged or not) before writing any MR as there are way too many subtle changes to keep track of. As presented in the previous comment, a test implementation is available at https://github.com/Derugon/types-mediawiki/tree/split, but for easier traceability and migration process it may be better to implement this proposal in multiple steps, so we can more easily discuss each part.

Implementation Proposal

  1. Expose all local types.
    • expose local jQuery plugin types by creating sub-namespaces within the JQuery namespace, namely JQuery.Client, JQuery.Color, and JQuery.textSelection,
    • expose local mediaWiki types and interfaces through existing classes and namespaces of mw (e.g. mw.Api, mw.loader),
    • remove redundant type prefixes (e.g. rename ApiOptions to Options, inside namespace mw.Api)
    • move utility types (e.g. TypeOrArray, GetOrDefault, NoReturn) to a utils.d.ts declaration file.
  2. Remove all existing sub-modules.
    • remove empty exports (export {};),
    • replace imports with /// <reference />,
    • only the root directory would remain a module, the root index.d.ts file would remain the same.
  3. Make mediawiki and jquery global libraries.
    • rename ./mw to ./mediawiki,
    • import oojs-ui and ./jquery types from ./mediawiki.
  4. Split mediawiki and jquery into ResourceLoader modules.
    • make one folder (jquery.* or mediawiki.*) per module,
    • move declarations to their corresponding module folder,
    • leave base declarations (such as mw.config or mw.loader) to mediawiki.
  5. Make this package a module type repository.
    • change recommandations in README, to mention typeRoots and types, users may still use include to not have the import auto-completion be flooded with RL modules,
    • add one index.d.ts module file per module folder, which would be filled according to the associated ResourceLoader entry, see example for module mediawiki.util below:
// ResourceLoader module dependencies
// including "mediawiki.base" if there is no other mw dependencies
import "../jquery.client";
import "../mediawiki.base";

// local declaration files
import "./RegExp.d.ts";
import "./util.d.ts";

// ResourceLoader module exports
export = mw.util;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant