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

Handle optional dependencies in loadLanguages() #1417

Merged
merged 6 commits into from
May 26, 2018

Conversation

Golmote
Copy link
Contributor

@Golmote Golmote commented May 4, 2018

Fixes #1393

@Golmote Golmote assigned mAAdhaTTah and unassigned mAAdhaTTah May 4, 2018
@Golmote Golmote requested a review from mAAdhaTTah May 4, 2018 06:23
@mAAdhaTTah
Copy link
Member

I'll probably be able to review this PR this weekend, just FYI.

@Golmote
Copy link
Contributor Author

Golmote commented May 4, 2018

Sure, take your time!

Copy link
Member

@mAAdhaTTah mAAdhaTTah left a comment

Choose a reason for hiding this comment

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

I think this is a really nice solution to this problem, and I think we can do it without exposing withoutDependencies (documented or otherwise). Let me know what you think!

@@ -1,23 +1,65 @@
var components = require('../components.js');

function loadLanguages(arr) {
function getOptionallyDependents(mainLanguage) {
return Object.keys(components.languages).filter(function (language) {
Copy link
Member

Choose a reason for hiding this comment

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

We do this a couple times; we could definitely do this once and remove meta and reuse that array. If we're mostly going to be doing iteration, we could just map over the keys and turn them into values, so end up w/ an array of languages. If we want to go a step further, we could tag the objects w/ a loading property and remove the withoutDependencies information and just recurse until you hit a component that's already being loaded.

This would be a bit more stateful but shouldn't be hard to implement and would allow us to hide the implementation details from consumers. I don't really love exposing withoutDependencies, if if it's not technically public API, and that would do away with it nicely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I understand your whole idea here.

We could indeed process the languages only once, though. We could build a structure that maps a language to its "peerDependencies" and therefore just access it by key. Something like:

var dependentsMap = null;
function getOptionallyDependents(mainLanguage) {
    if (!dependentsMap) {
        dependentsMap = {};
        /* Build a structure like:
        dependentsMap = {
            "fortran": ["pure"],
            "coffeescript": ["haml", "pug"],
            ...
        }
        */
    }
    return dependentsMap[mainLanguage] || [];
}

Is it what you meant?


require('./prism-' + language);
arr.forEach(function (language) {
if (components.languages[language]) {
Copy link
Member

Choose a reason for hiding this comment

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

If we change this to:

if (!components.languages[language]) {
  console.warn('Language does not exist ' + language);
  return;
}

...then we can save an indentation. I like early returns :)

components.json Outdated
@@ -59,7 +59,8 @@
},
"css": {
"title": "CSS",
"option": "default"
"option": "default",
"optionalDependencies": "markup"
Copy link
Member

Choose a reason for hiding this comment

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

peerDependencies maybe?


// Reload dependents
var dependents = getOptionallyDependents(language);
dependents = dependents.filter(function (dependent) {
Copy link
Member

Choose a reason for hiding this comment

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

One-liner these two?

return false;
});
if (dependents.length) {
loadLanguages(dependents, true);
Copy link
Member

Choose a reason for hiding this comment

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

Is the purpose of withoutDependents to avoid an infinite loop? Are we sure it recurses through everything? If it is, maybe we don't need it (see above).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually no, it wasn't to prevent an infinite loop. I think an infinite loop can still happen if we have circular dependencies, hopefully this is not the case at this time AFAIK.

It was supposed to be an optimisation, because you need to reload a component so that it takes its updated "peerDependencies" into account, but you don't need to reload the whole "inheritance" tree. There's already a lot of files being required there, so I tried to keep the amount as low as possible.

@Golmote
Copy link
Contributor Author

Golmote commented May 10, 2018

I updated the PR to address your comments.

Regarding withoutDependencies, I kept it for now, I still need to understand what you meant in your first comment. But I removed it from the export so it's not exposed anymore.
And the map of dependents is now built only once.

Let me know what you think.

@mAAdhaTTah
Copy link
Member

But I removed it from the export so it's not exposed anymore.

This may be fine; the idea is essentially to keep track of what languages we're in the process of loading and avoid reloading them if we hit them again via circular references. Doing it that way would ensure we recurse down all the way down instead of (what looks like?) one level down.

But maybe that's not actually an issue? This is certainly a simpler implementation.

@mAAdhaTTah
Copy link
Member

mAAdhaTTah commented May 15, 2018

Also, I think this PR may be needed by the babel plugin to ensure we load everything in the correct order.

@Golmote
Copy link
Contributor Author

Golmote commented May 26, 2018

@mAAdhaTTah Since we don't seem to have any circular dependencies at the moment, I would suggest we keep it this way, at least for now.
Did I address all your requested changes? It this good to be merged?

# Conflicts:
#	components.js
@mAAdhaTTah
Copy link
Member

Yeah, this is good. Just need to fix the conflict in components.js.

# Conflicts:
#	components.js
@Golmote Golmote merged commit 84935ac into PrismJS:gh-pages May 26, 2018
@Golmote Golmote deleted the load-languages branch May 26, 2018 13:36
mAAdhaTTah added a commit that referenced this pull request Jun 5, 2018
* gh-pages: (33 commits)
  Add double-class specificity hack (#1435)
  Moved tutorial link to the end of the list
  Make line-numbers styles more specific
  Fix mixed content warning
  Create CNAME
  Delete CNAME
  Update documentation for node & webpack usage
  Handle optional dependencies in `loadLanguages()` (#1417)
  Add `objectpascal` as an alias to `pascal` (see #1426)
  Add support for XQuery. Fix #1405 (#1411)
  Website: Fix Download page not handling multiple dependencies when from Redownload URL
  JSX: Add support for fragments short syntax. Fix #1421
  Support for Template Toolkit 2 (#1418)
  ASP.NET should require C#
  Run gulp
  Move guard into conditional and check for language
  Don't process language if block language not set
  JSX: Allow for two levels of nesting inside JSX tags. Fix #1408
  Add missing reference to issue in specific test.
  Powershell: Allow for one level of nesting in expressions inside strings. Fix #1407
  ...

# Conflicts:
#	components/prism-kotlin.js
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.

2 participants