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

Watch Inclusions #2675

Closed
Metaxona opened this issue Aug 19, 2024 · 8 comments
Closed

Watch Inclusions #2675

Metaxona opened this issue Aug 19, 2024 · 8 comments
Milestone

Comments

@Metaxona
Copy link

Search terms

Watch

Expected Behavior

I expected watch to watch the files changes on

  • typedoc.json
  • config.projectDocuments files
  • readme -> when not none

Actual Behavior

changes made to the said files included above does not re-execute the build unless i quit watch mode and re-execute the command again

Steps to reproduce the bug

if this is not a normal behavior tag me on this issue so i can provide a repo that recreates this bug, if this is the normal behavior
converting this to a feature request would be the next choice

Environment

  • Typedoc version: ^0.26.5
  • TypeScript version: ^5.5.4
  • Node.js version: v20.11.0
  • OS: Ubuntu 22.04.4 LTS x86 (Specifics: Ubuntu Server)
@Metaxona Metaxona added the bug Functionality does not match expectation label Aug 19, 2024
@Gerrit0
Copy link
Collaborator

Gerrit0 commented Aug 19, 2024

This is expected as watch mode just uses typescript's watch with a hook to generate docs, there isn't any support for watching other changes. This is essentially the same limitation as what is preventing #1772

@Metaxona
Copy link
Author

so currently there is no way to solve it?

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Aug 20, 2024

Correct, it isn't supported yet

@pjeby
Copy link
Contributor

pjeby commented Dec 7, 2024

For anyone encountering this issue, you can work around it using the onchange package from npm, and a build script like this:

"watch-docs": "onchange --await-write-finish 3000 -i --kill \"*.md\"  \"guides/**/*.md\" \"typedoc.config.*\" \"typedoc/*\" -- typedoc --watch",

Of course you need to change the patterns and command line to something that makes sense for your project. (e.g. the typedoc/* part is to catch changes to my custom CSS, and you might not even have such a directory.)

The --await-write-finish bit says to wait 3 seconds after a change to these files to make sure you're not changing something else, to avoid continually killing and restarting the process (e.g. if you have a build step that writes to your .md files or custom.css.)

@shawninder
Copy link
Contributor

If this isn't fixable, maybe a quick note about it in the docs would help?

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Dec 18, 2024

There is a note - https://typedoc.org/documents/Options.Other.html#watch

Note: This mode will only detect changes to files watched by the TypeScript compiler. Changes to other files (README.md, imported files with @include or @includeCode) will not cause a rebuild.

@pjeby
Copy link
Contributor

pjeby commented Jan 26, 2025

I did a little experimentation with the TS compiler API, and it has a way to add files to watch and get a callback when the files change.

diff --git a/src/lib/application.ts b/src/lib/application.ts
index e17243c1..7a13ba39 100644
--- a/src/lib/application.ts
+++ b/src/lib/application.ts
@@ -506,6 +506,7 @@ export class Application extends AbstractComponent<

         let successFinished = true;
         let currentProgram: ts.Program | undefined;
+        let watches: ts.FileWatcher[] = []

         const runSuccess = () => {
             if (!currentProgram) {
@@ -528,6 +529,15 @@ export class Application extends AbstractComponent<
                     return;
                 }
                 const project = this.converter.convert(entryPoints);
+                watches.forEach(w => w.close())
+                watches = []
+                for(const d of project.documents || []) {
+                    const path = project.files.getReflectionPath(d)
+                    console.log("watching", path)
+                    if (path) watches.push(host.watchFile(path, () => {
+                        console.log("changed", path)
+                    }))
+                }
                 currentProgram = undefined;
                 successFinished = false;
                 void success(project).then(() => {

With the above patch, I was able to get TS to output "changed" notices for files that were document sources. I'm not entirely sure where to take the patch from there because the intended logic of runSuccess() eludes me a bit here - it has a lot of self-reference and state flags. (And presumably it would make sense to only update the changed document reflections and then rerun the success part, rather than rerunning the converter, if the currentProgram hasn't changed.) Last, but not least, there's a bit of a headache in the form of needing debouncing, as the watch callback can be invoked 3 or 4 times for one save of a changed file.

Any hints on how to expand this into an acceptable PR would be welcome, as I'd love to have the feature. My current impression is that runSuccess is the way it is because it's in some sense trying to intelligently merge two async producers: the output from the TypeScript compiler, and the async operation of the output generation. That is, it can get a call from the afterProgramCreate callback while an output generation is in progress, and so needs to not run a second success operation while the first is in progress.

So, if I'm still understanding correctly, a debounced watch callback would want to check if there's a currentProgram, and if so, ignore the notice, because it's going to do a full regen anyway. And if successFinished is false, it needs to wait for the last output generation and then start a new one. (After first either re-running the convert step or just recompiling the changed document reflection somehow.)

Does that sound right?

@pjeby
Copy link
Contributor

pjeby commented Jan 26, 2025

Got this version working, at least on a small project:

diff --git a/src/lib/application.ts b/src/lib/application.ts
index e17243c1..f38fa442 100644
--- a/src/lib/application.ts
+++ b/src/lib/application.ts
@@ -506,6 +506,7 @@ export class Application extends AbstractComponent<

         let successFinished = true;
         let currentProgram: ts.Program | undefined;
+        let watches: ts.FileWatcher[] = []

         const runSuccess = () => {
             if (!currentProgram) {
@@ -528,6 +529,18 @@ export class Application extends AbstractComponent<
                     return;
                 }
                 const project = this.converter.convert(entryPoints);
+                watches.forEach(w => w.close())
+                watches = []
+                const lastProgram = currentProgram;
+                for(const d of project.documents || []) {
+                    const path = project.files.getReflectionPath(d)
+                    if (path) watches.push(host.watchFile(path, () => {
+                        if (!currentProgram) {
+                            currentProgram = lastProgram
+                            if (successFinished) runSuccess()
+                        }
+                    }))
+                }
                 currentProgram = undefined;
                 successFinished = false;
                 void success(project).then(() => {

The idea is it fakes the arrival of a new currentProgram (by setting it to the previous one) if there isn't one already (because if there is, a rerun is already scheduled), and restarts generation if it isn't already running.

Presumably this could be extended in a straightforward way to handle at least the README, but config files would require restarting the whole program I think.

(Edit note: tweaked the logic slightly so document changes happening during the output writing will still flag the need for another pass.)

pjeby added a commit to pjeby/typedoc that referenced this issue Jan 26, 2025
This PR adds preliminary watch support for:

- Project README
- Project documents
- Custom CSS file
- Custom JS file

It does *not* currently support:

- Other Asset files
- Config files
- package.json
- Anything else!

With appropriate added options, it could probably be made to
support arbitrary additional watch files or folders.
pjeby added a commit to pjeby/typedoc that referenced this issue Jan 26, 2025
@Gerrit0 Gerrit0 added this to the v0.27.7 milestone Feb 2, 2025
@Gerrit0 Gerrit0 removed the bug Functionality does not match expectation label Feb 2, 2025
@Gerrit0 Gerrit0 closed this as completed Feb 9, 2025
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

4 participants