jleyba / js-dossier

A JavaScript documentation generation tool.
Apache License 2.0
150 stars 15 forks source link

FR: Some way to hide public functions that depend on private (or package-private) types??? #94

Open shicks opened 7 years ago

shicks commented 7 years ago

The primary use here is "private constructors", which currently are not well-supported by Closure Compiler. One way to fake this is by adding an extra parameter to the constructor, so that it requires a non-null module-local (or package-private) type, guaranteeing that nobody else can call the constructor directly. In these cases, it would be great to simply hide the constructor from the documentation.

That said, this idea is only half-baked, since there are presumably plenty of legitimate cases for instances of the module-local type to leak out and then these functions could indeed be called. I don't have any good ideas for ways around this, so feel free to close if you think this is infeasible to do "correctly".

myphysicslab commented 7 years ago

I'm curious because I have private constructors which are working well. Here is an example:

/**
* @constructor
* @final
* @struct
* @implements {myphysicslab.lab.engine2D.RigidBody}
* @private
*/
myphysicslab.lab.engine2D.Scrim = function() {};

var Scrim = myphysicslab.lab.engine2D.Scrim;

/**
* @type {!myphysicslab.lab.engine2D.Scrim}
* @private
*/
Scrim.singleton = new Scrim();

The constructor appears in the docs with a big red "private" box to the left of the constructor name; and all the docs for the constructor are listed, which is what I need.

There seems to be no way to control whether private or protected entities appear in the Dossier docs at present. For me it is currently working just right:

jleyba commented 7 years ago

This issue is actually fairly nuanced. For starters, closure doesn't differentiate between "private constructor" and "private type". So right now, dossier treats things as "public type with a private constructor".

Another interesting case is modules whose public API uses an "internal" type that's never explicitly exported. Here's an example:

class Options {}

/** @return {!Options} */
export function newOptions() { return new Options; }

export class Job {
  /** @param {!Options} options */
  constructor(options) {
  }
}

Dossier will document these type references, but (a) the names will be mangled and (b) a formal page will not be generated for the type (see #93).

I think I need to add a two extra knobs to Dossier's config:

  1. The minimum visibility to include in generated documentation (private, package, protected, public).
  2. Whether classes should always be considered public and visibility applies to the constructor only, or if the constructor visibility applies to the type as well

For the other issue, I'll probably need to compute reachability -- if a type is returned by a public API, it is effectively public. That, or generate an error when a public API references non-public types.

shicks commented 7 years ago

Closure Compiler is working on plumbing ES6 all the way through the compiler. Once that's done, constructors will no longer necessarily be conflated with the class itself, so it could potentially become meaningful to tag the constructor private for an otherwise-public class.

jleyba commented 7 years ago

Will that plumbing depend on NTI?

shicks commented 7 years ago

Not directly. They're related in that passes that depend on type information will probably want to only migrate in NTI. But in terms of usability for dossier, it should be enough that class declarations just not be rewritten, I think. OTI and NTI do have very different visibility checkers, but maybe that's not relevant?

jleyba commented 7 years ago

No, that shouldn't matter. As long as NTI doesn't drop JSDocInfo, dossier should be OK.