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

Automatically convert GitHub-based projects into MarkBind websites #698

Merged
merged 17 commits into from
Apr 12, 2019

Conversation

amad-person
Copy link
Contributor

@amad-person amad-person commented Feb 17, 2019

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] New feature

Fixes #675. Part of #660.

What is the rationale for this request?

Provides a way to convert an existing Github wiki or a docs folder into a MarkBind website. It will be easier to integrate MarkBind into the workflow for Github-based project.

What changes did you make? (Give an overview)

  1. Added a new option for the init command: --convert/-c. Users can run markbind init -c in the desired directory to convert it into a MarkBind project.
  2. Converting the folder into a MarkBind project includes the following:
    • Running a usual markbind init.
    • Then, copying over contents of README.md (or Home.md) to index.md
    • Adding an about.md page if not present.
    • Adding a site navigation menu to all pages by updating navigation.md in the default layout. As mentioned in Automatically convert GitHub Wikis to MarkBind sites #675, if _Sidebar.md is present in the root directory, this will be used for the site navigation menu. Otherwise a menu will be generated.
    • Adding a footer to the website by updating footer.md in the default layout. As mentioned in Automatically convert GitHub Wikis to MarkBind sites #675, if _Footer.md is present in the root directory, this will be used for the site navigation menu. Otherwise the default footer generated by markbind init will be used.
    • Adding a top navigation bar to all pages. This is done by adding a nav bar into header.md in the default layout.
    • Updating site config so all pages have the default layout applied to them.

Is there anything you'd like reviewers to focus on?

  • Correctness of test.bat file.

Testing instructions:

Run markbind init -c in a folder of your choice and build the site. Run npm run test(win) to run the UTs and FT.

Proposed Commit Message:

Automatically convert GitHub-based projects into MarkBind websites

GitHub projects commonly have wikis or docs folders for project
documentation. As MarkBind is especially optimized as a project
documentation tool, we would want to make it easier for projects to
adopt it in their workflow.

Let's add the ability to automatically convert a GitHub wiki or docs
folder into a MarkBind website.

@amad-person amad-person marked this pull request as ready for review March 2, 2019 10:06
@amad-person
Copy link
Contributor Author

  1. I'm not sure where to document this functionality. Should I add information in MarkBind in the Project Workflow and include a link to the section in CLI Commands?

  2. For testing this, should I add a dummy folder and provide an expected output folder (similar to our functional testing)?

@damithc
Copy link
Contributor

damithc commented Mar 2, 2019

  1. I'm not sure where to document this functionality. Should I add information in MarkBind in the Project Workflow and include a link to the section in CLI Commands?

That sounds about right.

@nicholaschuayunzhi nicholaschuayunzhi self-requested a review March 3, 2019 08:27
Copy link
Contributor

@nicholaschuayunzhi nicholaschuayunzhi left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @amad-person. I've left some comments and potential improvements. Overall, I think the process is sound and I've tested it on some wikis. I think the thing that bugs me the most is including the header in every addressable page. Not sure if layouts/front matter can help us here.

Do remember to write the documentation and tests! When you're done remember to remove the [WIP] tag in the PR title. Once again thanks for the effort!

@Xenonym
Copy link
Contributor

Xenonym commented Mar 4, 2019

If you try --convert with zaproxy/zaproxy/wiki, it will fail out of the box as the page FAQformauth contains the text username={%username%}&password={%password%}, which Nunjucks tries and fails to parse as variables:

error: uncaughtException: (C:\Users\user\Desktop\zaproxy.wiki\FAQformauth.md) [Line 31, Column 114]
  unknown block tag: username date=Mon Mar 04 2019 16:21:20 GMT+0800 (Malay Peninsula Standard Time), pid=27616, uid=null, gid=null, cwd=C:\Users\user\Desktop\zaproxy.wiki, execPath=C:\Program Files\nodejs\node.exe, version=v8.15.0, argv=[C:\Program Files\nodejs\node.exe, C:\Program Files\nodejs\node_modules\markbind-cli\index.js, serve], rss=90669056, heapTotal=78016512, heapUsed=46236248, external=927768, loadavg=[0, 0, 0], uptime=142101
Template render error: (C:\Users\user\Desktop\zaproxy.wiki\FAQformauth.md) [Line 31, Column 114]
  unknown block tag: username
    at Object.exports.prettifyError (C:\Users\user\Documents\GitHub\CS3281-2\markbind\node_modules\nunjucks\src\lib.js:34:15)
    at new_cls.render (C:\Users\user\Documents\GitHub\CS3281-2\markbind\node_modules\nunjucks\src\environment.js:472:27)
    at new_cls.renderString (C:\Users\user\Documents\GitHub\CS3281-2\markbind\node_modules\nunjucks\src\environment.js:328:21)
    at Object.module.exports.renderString (C:\Users\user\Documents\GitHub\CS3281-2\markbind\node_modules\nunjucks\index.js:80:14)
    at fs.readFile (C:\Users\user\Documents\GitHub\CS3281-2\markbind\src\lib\markbind\src\parser.js:508:36)
    at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:511:3)

We should either parse the wiki pages to "escape" problematic syntax, or at least document that --convert may not work if there is MarkBind/Nunjucks syntax in wiki pages.

@yamgent
Copy link
Member

yamgent commented Mar 6, 2019

We should either parse the wiki pages to "escape" problematic syntax, or at least document that --convert may not work if there is MarkBind/Nunjucks syntax in wiki pages.

I think in general, when we fail a conversion, we should just retain the original .md file content, but print a warning to say that we have failed for this .md, and continue on with our business.

For edge cases in general, we can try to apply fixes wherever possible, a good rule of thumb is that if the fix is not too complicated in code, and the fix is not "controversial" in nature, then we can consider handling the edge case. Otherwise we shouldn't worry about them too much.

For this edge case though, while I think escaping could be a good try, the problem is that there is no known consistent way of escaping Nunjucks stuff (this is a known bug btw, we have always been abusing <span> in the past to get around the problem of Nunjucks interpreting stuff that is not supposed to be interpreted as Nunjucks code).

@amad-person amad-person changed the title [WIP] Add ability to convert Github wiki to MarkBind site [WIP] Add ability to convert Github wiki/docs to MarkBind site Mar 7, 2019
@amad-person
Copy link
Contributor Author

Currently, the conversion process doesn't parse the .md files, so errors (if any) will arise only on markbind build.

Any ideas for getting these build errors? Maybe I can parse the files before starting the conversion and log the errors - so users can fix them and start again?

@nicholaschuayunzhi
Copy link
Contributor

Personally I feel that if we handle nunjucks error well, the author would be able to identify the problem and adjust accordingly? Perhaps this isn't in the scope of this feature? @yamgent Do you agree?

@yamgent
Copy link
Member

yamgent commented Mar 7, 2019

Personally I feel that if we handle nunjucks error well, the author would be able to identify the problem and adjust accordingly? Perhaps this isn't in the scope of this feature? @yamgent Do you agree?

I agree that this PR shouldn't be going inside the nunjucks code and modify stuff for this current edge case, the fixing of the errors should be done manually by the author. Printing a user-friendly message will make that experience better, so I agree that this isn't the scope of the current PR.

@amad-person
Copy link
Contributor Author

So, would adding the following in this PR be okay?

  1. Printing a message to the console

    --convert may not work if there is MarkBind/Nunjucks syntax

    (for all cases, whether there might be build errors or not)

  2. Adding this warning to the documentation.

@yamgent
Copy link
Member

yamgent commented Mar 8, 2019

  1. Printing a message to the console

    --convert may not work if there is MarkBind/Nunjucks syntax

    (for all cases, whether there might be build errors or not)

I don't think this message would be very useful, such messages are generally not paid attention to, because it works, the error message is redundant, and if it doesn't, users will see the error churned out by nunjucks and may not make the connections between the error and this message.

The documentation is fine and we should definitely include it.

@amad-person
Copy link
Contributor Author

Updates after discussion -
Print a message to let users know they can update {{ baseUrl }}.
Two formats of site navigation files need to be supported:

  1. [[Name|URL]]
  2. [Name](URL)

@amad-person amad-person force-pushed the add-convert-wiki branch 2 times, most recently from 020c49d to faeab48 Compare March 26, 2019 11:50
Copy link
Contributor

@nicholaschuayunzhi nicholaschuayunzhi left a comment

Choose a reason for hiding this comment

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

Thanks for the changes @amad-person. I've left some brief comments on the things we went through during the meeting.

I also think that you have a misunderstanding on how some of the js features work. I've left some comments wrt to one code snippet. Please let me know if it was unclear.

Also do remember to write tests and remove [WIP] in your PR title when you are ready for review.

src/Site.js Outdated
const updatedAddressablePageContent = includeTopNavCode + addressablePageContent;
return fs.outputFileAsync(addressablePagePath, updatedAddressablePageContent);
}
return Promise.all(outputFilesPromises);
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume what you are trying to do is to create an array of promises called outputFilesPromises, after which calling Promise.all(outputFilesPromises so that when all pages have been written, it will continue down the promise chain.

I think there are 2 main problems with this code.

  1. forEach (line 598)
    • outputFilesPromises will be undefined because forEach would return undefined
    • you should use map instead
  2. Promise.all (line 607)
    • Promise.all takes in an array (iterable) of promises. Only when all the promises are resolved would it resolve and continue down the promise chain.
    • returning this in the for loop does not do any assignment
    • does not pass this promise down the chain. As such, the next .then will execute before all the files have been output.

i believe the code that should be written is

const outputFilesPromises = this.addressablePages
    .filter(addressablePage => !addressablePage.src.startsWith('_'))
    .map((page) => {       
        const addressablePagePath = path.join(this.rootPath, page.src);
        const topNavRelativePath = path.relative(this.rootPath, topNavDefaultPath);
        if (topNavRelativePath.length > 0) {
            const includeTopNavCode = `<include src="${topNavRelativePath}" />\n\n`;
            const addressablePageContent = fs.readFileSync(addressablePagePath, 'utf8');
            const updatedAddressablePageContent = includeTopNavCode + addressablePageContent;
            return fs.outputFileAsync(addressablePagePath, updatedAddressablePageContent);
        }
        return Promise.resolve();
     });
return Promise.all(outputFilesPromises);

Copy link
Contributor

Choose a reason for hiding this comment

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

In addition, some statements can be moved out of map.

const topNavRelativePath = path.relative(this.rootPath, topNavDefaultPath);
if (topNavRelativePath.length == 0) {
    return Promise.resolve();
}
const includeTopNavCode = `<include src="${topNavRelativePath}" />\n\n`;
const outputFilesPromises = this.addressablePages
    .filter(addressablePage => !addressablePage.src.startsWith('_'))
    .map((page) => {       
        const addressablePagePath = path.join(this.rootPath, page.src);
        const addressablePageContent = fs.readFileSync(addressablePagePath, 'utf8');
        const updatedAddressablePageContent = includeTopNavCode + addressablePageContent;
        return fs.outputFileAsync(addressablePagePath, updatedAddressablePageContent);
    });
return Promise.all(outputFilesPromises);

This function modifies the source files?

Copy link
Contributor

Choose a reason for hiding this comment

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

This function inserts a top nav to every addressable page. I dont think there's a clean way to insert a top nav without modifying the original wiki site in some way.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, this is specific to conversion. If not entirely possible to abstract this out of Site, then shall we at least put this in a different file (e.g. Site.convert.js)? We can expect the caller to require that file after Site.js.

Copy link
Contributor

Choose a reason for hiding this comment

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

@amad-person could you update the feature based on #698 (comment) ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@acjh @yamgent how should we handle the constants in Site.js used in the conversion feature. It seems like defining constants in that manner is not good for modularity.

Should we:

  • Make the used constants static variables of Site
  • Copy the constants in Site.convert.js and refactor in the future
  • Keep the feature in Site and refactor everything in one shot

Copy link
Member

Choose a reason for hiding this comment

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

  • Copy the constants in Site.convert.js and refactor in the future

This seems like a pretty good option

Copy link
Contributor

Choose a reason for hiding this comment

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

Projects usually have file(s) for constants, e.g. https://github.com/vuejs/vue/blob/dev/src/shared/constants.js

Let's start with src/constants.js.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the input @acjh @yamgent. From discussion today, we already have an issue tracking this #597. Perhaps the change is out of scope of the pull request? Perhaps we shall do all the refactoring in one shot and when there is less on going development at the moment.

So to be clear what i mean is that the conversion logic will be be in Site.js.

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 cool with it.

Copy link
Contributor

@nicholaschuayunzhi nicholaschuayunzhi left a comment

Choose a reason for hiding this comment

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

Thanks for the changes and sorry for late review. I noted that if my test fails, the removal of unwanted directories is not done. Additionally I've left some comments.

As for the styling of the side nav and footer I agree that it can be done in another PR and is also a candidate for good first issue.

expect(fs.existsSync(path.resolve('inner/_markbind/layouts/default/footer.md'))).toEqual(true);

// site navigation
expect(fs.existsSync(path.resolve('inner/_markbind/layouts/default/navigation.md'))).toEqual(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this addressed else where?

If no I was thinking of doing a check on the content of the navigation file here. The file is generated by the conversion and the results should be predictable.

@amad-person amad-person dismissed stale reviews from yamgent via c01a988 April 10, 2019 15:39
@nicholaschuayunzhi
Copy link
Contributor

@amad-person thanks for the PR. Could you write a a Proposed Commit message for the PR (to be updated in the PR description)?

@yamgent yamgent added this to the v2.1.2 milestone Apr 12, 2019
@amad-person
Copy link
Contributor Author

@nicholaschuayunzhi @yamgent updated PR description with the proposed commit message.

@yamgent yamgent changed the title Add ability to convert Github wiki/docs to MarkBind site Automatically convert GitHub-based projects into MarkBind websites Apr 12, 2019
@yamgent yamgent merged commit db4ce52 into MarkBind:master Apr 12, 2019
@amad-person amad-person deleted the add-convert-wiki branch December 7, 2019 07:27
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

Successfully merging this pull request may close these issues.

Automatically convert GitHub Wikis to MarkBind sites
7 participants