11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
16.67k stars 484 forks source link

Add version of template language plugin packages on 11ty docs #906

Open cfjedimaster opened 4 years ago

cfjedimaster commented 4 years ago

Please pardon me if this rambles a bit. I will absolutely be ok breaking this into smaller issues, ERs, and so forth. This is kind of broad and I wanted a place to lay out some thoughts and get feedback. I also want to help with this as well so please take this as me volunteering as well. Anyway, sorry for the long pre-amble.

I was helping out a user on Twitter who was trying to use something in Liquid that is only supported in a new version than 11ty ships.

So question 1 - where do we document the version of included libraries? At least for Liquid (https://www.11ty.dev/docs/languages/liquid/) it is not documented. I'm sure it exists somewhere, but it should absolutely be documented on each individual template language page. You do specify how to supply your own, but as a user, I don't even know what version is being included.

Question 2- based on this issue (https://github.com/11ty/eleventy/issues/469), is there a "policy" for Eleventy in terms of how to handle updates? So for example, if Eleventy used Liquid version 1.01, and a security issue was found and fixed in 1.02, I assume Eleventy would quickly bring that in, right?

If v2 comes out and has no breaking changes, when would you roll it in? Do you check like once a month or so, or wait for a PR?

If v2 has breaking changes, it looks like you attempt to make it backwards compat. That's noble, but I have to say I'd also (well, as one user of Eleventy :) be ok with the understanding that my code may break if I upgrade Eleventy and it includes a newer Liquid. If it happened a lot, I'd blame Liquid more than Eleventy, but I can totally understand where you (you being everyone behind Eleventy) may not want that grief.

Question 3 - it feels like this issue as a whole should be it's documentation page. Ie, "This is how we handle versions, updates, breaking changes, etc." Agreed? If so I'd volunteer to take a stab at writing it.

Thank you for listening.

DirtyF commented 4 years ago

npm outdated is no docs, but here's the current state:

Package      Current  Wanted  Latest  Location
ejs            2.7.4   2.7.4   3.0.1  @11ty/eleventy
handlebars     4.7.1   4.7.3   4.7.3  @11ty/eleventy
liquidjs       6.4.3   6.4.3   9.6.2  @11ty/eleventy
mustache       2.3.2   2.3.2   4.0.0  @11ty/eleventy

Maybe the current version of each templating languages used in the package.json could be extracted and displayed in the docs?

I don't know how strictly Eleventy adheres to semantic versioning, but anything before v1.0 should be considered unstable right?

Ryuno-Ki commented 4 years ago

Fun fact: npm has a --json flag, which produces machine-readable output:

npm outdated --json (undocumented IIRC).

This is how we handle versions, updates, breaking changes, etc.

I'd expect that to be a markdown file.

pdehaan commented 4 years ago

FYI, npm outdated is documented here: https://docs.npmjs.com/cli-commands/outdated.html

zachleat commented 3 years ago

strictly Eleventy adheres to semantic versioning

Eleventy will adhere to semantic versioning but ignore the pre-v1.0 exceptions because I think those are silly. Our next version will be 1.0 to make this more consistent.

cfjedimaster commented 3 years ago

To handle part one of this issue, and assuming it can't be automatic, may I do a PR on the docs that adds the version #s for template language libraries?

cfjedimaster commented 3 years ago

Ran into this again today. :) Once 1.0 lands, I'd like to revisit this and add the explicit versions to the docs.

pdehaan commented 3 years ago

Probably doesn't help anybody, but I was curious how easy [part of] this would be to scrape from the package-lock.json file on a clean install and via npm info [package name] --json:

@11ty/eleventy v0.12.1

Package Requested Actual Latest
ejs ^2.7.4 2.7.4; 2019-11-19 3.1.6; 2021-02-06
hamljs ^0.6.2 0.6.2; 2014-02-17 0.6.2
handlebars ^4.7.7 4.7.7; 2021-02-15 4.7.7
liquidjs ^6.4.3 6.4.3; 2019-02-13 9.25.1; 2021-06-19
nunjucks ^3.2.3 3.2.3; 2021-02-15 3.2.3
pug ^3.0.2 3.0.2; 2021-02-28 3.0.2

@11ty/eleventy v1.0.0-canary.37

Package Requested Actual Latest
ejs ^3.1.6 3.1.6; 2021-02-06 3.1.6
hamljs ^0.6.2 0.6.2; 2014-02-17 0.6.2
handlebars ^4.7.7 4.7.7; 2021-02-15 4.7.7
liquidjs ^9.23.2 9.25.1; 2021-06-19 9.25.1
nunjucks ^3.2.3 3.2.3; 2021-02-15 3.2.3
pug ^3.0.2 3.0.2; 2021-02-28 3.0.2
const cp = require("child_process");

const packageLock = require("./package-lock.json");

const packages = ["ejs", "hamljs", "handlebars", "liquidjs", "nunjucks", "pug"]

main("@11ty/eleventy", packages);

async function main(name="", packages=[]) {
  const eleventy = packageLock.dependencies[name];

  console.log(`${name} v${eleventy.version}\n`);
  console.log("| Package | Requested | Actual | Latest |");
  console.log("|:--------|:---------:|:-------|:-------|");
  for (const package of packages) {
    const version = packageLock.dependencies[package].version;
    const info = getPackageInfo(package);
    const pkgTime = info.time[version];
    const latest = info["dist-tags"].latest;
    console.log(`| ${package} | ${eleventy.requires[package]} | ${version}; ${localeDate(pkgTime)} | ${latest}${version !== latest ? `; ${localeDate(info.time[latest])}` : ""}`);
  }
}

function getPackageInfo(name) {
  const res = cp.execSync(`npm info ${name} --json`);
  return JSON.parse(res);
}

function localeDate(date) {
  date = new Date(date);
  return date.toLocaleDateString();
}

Usually if I need to remember which exact/resolved version of liquidjs I'm using in 11ty stable/v0.12 channel, I just do something like this (either in my .eleventy.js config for debugging, or I'll export it in a global data file if I plan on exposing it to 11ty templates):

const {name, version} = require("liquidjs/package.json");
console.log(name, version);
pdehaan commented 3 years ago

Turns out I can use pacote and david to check for outdated modules without even having to install Eleventy locally, which means it's pretty easy to quickly check v0.10, latest, canary, etc.

const promisify = require("util").promisify;

const david = require("david");
const pacote = require("pacote");

// Convert callback API to promises.
const getDependencies = promisify(david.getDependencies);

main("@11ty/eleventy@canary", ["ejs", "hamljs", "handlebars", "liquidjs", "nunjucks", "pug"]);

async function main(nameVersion, pkgFilter) {
  const manifest = await pacote.manifest(nameVersion);
  const deps = await getDependencies(manifest, {stable: true});
  console.log(`${manifest.name} v${manifest.version}`);
  listDependencies(deps, pkgFilter);
}

function listDependencies(deps, pkgFilter) {
  // Flatten and sort deps object.
  deps = Object.entries(deps)
    .map(([name, obj]) => Object.assign({name}, obj))
    .sort((a, b) => a.name.localeCompare(b.name));

  if (Array.isArray(pkgFilter)) {
    deps = deps.filter(dep => pkgFilter.includes(dep.name));
  }

  console.log("Name | Required | Stable | Latest\n-----|----------|--------|-------");
  for (const dep of deps) {
    console.log(`${dep.name} | ${dep.required || "*"} | ${dep.stable || "None"} | ${dep.latest}`);
  };
}

@11ty/eleventy v0.12.1

Name Required Stable Latest
ejs ^2.7.4 3.1.6 3.1.6
hamljs ^0.6.2 0.6.2 0.6.2
handlebars ^4.7.7 4.7.7 4.7.7
liquidjs ^6.4.3 9.25.1 9.25.1
nunjucks ^3.2.3 3.2.3 3.2.3
pug ^3.0.2 3.0.2 3.0.2

@11ty/eleventy v1.0.0-canary.37

Name Required Stable Latest
ejs ^3.1.6 3.1.6 3.1.6
hamljs ^0.6.2 0.6.2 0.6.2
handlebars ^4.7.7 4.7.7 4.7.7
liquidjs ^9.23.2 9.25.1 9.25.1
nunjucks ^3.2.3 3.2.3 3.2.3
pug ^3.0.2 3.0.2 3.0.2

Although, that just tells me about package.json manifest and it's semver range. Not sure if there is an easy/good way to determine which final version of a package would be installed without, like, installing Eleventy and seeing which final version of LiquidJS got installed. For example, on Eleventy 0.12.1, it will install a version of liquidjs that satisfies ^6.4.3, but david seems to tell me the latest stable release number (which is 9.25.1, which isn't really relevant since that isn't in our semver range).


UPDATE: It's too hot outside, so I think I solved my issue using pacote and semver:

const pacote = require("pacote");
const semver = require("semver");

main("@11ty/eleventy", ["ejs", "hamljs", "handlebars", "liquidjs", "nunjucks", "pug"]);

async function main(pkgName="", engines=[]) {
  const packument = await pacote.packument(pkgName);
  const distTags = packument["dist-tags"];
  for (const [distTag, version] of Object.entries(distTags)) {
    if (distTag === "latest") {
      continue;
    }
    if (semver.lt(version, distTags.latest)) {
      // Delete a dist-tag if it is older than the "latest" stable release version.
      delete distTags[distTag];
    }
  }

  for (const [tag, version] of Object.entries(distTags)) {
    const v = packument.versions[version];
    v.tag = tag;
    console.log(`\n${v.name} v${v.version} (${tag})`);
    for (const engine of engines) {
      const engineVer = v.dependencies[engine];
      const maxVer = await getMaxSatisfyingSemver(engine, engineVer);
      console.log(`  ${engine}@${maxVer} (via ${engineVer})`);
    }
  }
}

async function getMaxSatisfyingSemver(name, pkgSemver) {
  const packument = await pacote.packument(`${name}@${pkgSemver}`);
  const engineVersions = Object.keys(packument.versions);
  return semver.maxSatisfying(engineVersions, pkgSemver);
}

OUTPUT

@11ty/eleventy v0.12.1 (latest)
  ejs@2.7.4 (via ^2.7.4)
  hamljs@0.6.2 (via ^0.6.2)
  handlebars@4.7.7 (via ^4.7.7)
  liquidjs@6.4.3 (via ^6.4.3)
  nunjucks@3.2.3 (via ^3.2.3)
  pug@3.0.2 (via ^3.0.2)

@11ty/eleventy v1.0.0-canary.37 (canary)
  ejs@3.1.6 (via ^3.1.6)
  hamljs@0.6.2 (via ^0.6.2)
  handlebars@4.7.7 (via ^4.7.7)
  liquidjs@9.25.1 (via ^9.23.2)
  nunjucks@3.2.3 (via ^3.2.3)
  pug@3.0.2 (via ^3.0.2)
DirtyF commented 3 years ago

Nice. Could console.table be of help here? https://developer.mozilla.org/en-US/docs/Web/API/Console/table

pdehaan commented 3 years ago

Nice. Could console.table be of help here? developer.mozilla.org/en-US/docs/Web/API/Console/table

Possibly, if we wanted to add some CLI flag that would display the various engine version numbers for the currently installed Eleventy.


I was inspired to build https://github.com/pdehaan/11ty-engine-versions over the weekend, which tries to fetch some package metadata and figure out version numbers like in my nonsense code above (but now it's a library I can install via npm/GitHub). Plus, this way I can use it with an Eleventy global data file and/or pagination, like https://pdehaan.github.io/11ty-engine-versions-test/.

I think one of my biggest concerns is that sub dependency versions can change at any time, so the page might need to be rebuilt occasionally (or I could create a GitHub Action on a cron job which rebuilds and pushes to GitHub pages nightly). Or maybe it will inspire me to investigate the new Eleventy Serverless stuff @zachleat has been working on.

zachleat commented 2 years ago

Just to circle back here on template engine breaking features, it depends on a few things!

We do document all major version bumps in our dependencies (not just for template languages) and the reason provided by the dependency for that bump: e.g. https://github.com/11ty/eleventy/releases/tag/v0.12.0 and https://github.com/11ty/eleventy/releases/tag/v0.11.0

If it’s a Node minimum version in the dependency that is already below our advertised Node support, I would include that and would not consider it an Eleventy breaking change.

For 1.0 we will have an upgrade helper plugin: https://github.com/11ty/eleventy-upgrade-help

Does that sufficiently answer the questions here?

zachleat commented 2 years ago

This is an automated message to let you know that a helpful response was posted to your issue and for the health of the repository issue tracker the issue will be closed. This is to help alleviate issues hanging open waiting for a response from the original poster.

If the response works to solve your problem—great! But if you’re still having problems, do not let the issue’s closing deter you if you have additional questions! Post another comment and we will reopen the issue. Thanks!

cfjedimaster commented 2 years ago

"Does that sufficiently answer the questions here?" I'd say no, because in order for a developer to find out what version Eleventy is using, they would need to go into the release tag notes, when it really should be in the top level docs for each template, right?

So for example, when I go here: https://www.11ty.dev/docs/languages/liquid/ I'd like to see, towards the top and very clear: Eleventy ships with Liquid version X.X.

To me it makes sense for that kind of information to be more readily available. Yes, it -is- available now, but let's float it up a bit. :)

zachleat commented 2 years ago

Good feedback! I think the action item here then is to prominently display the current version of each template language on the docs on their individual pages

pdehaan commented 2 years ago

Not sure how this works though. Should the docs say "Eleventy 0.12.1 ships with LiquidJS ^6.4.3" (with a generic semver range from the package.json)? 3rd party dependency versions can change frequently, and I'm not sure if the docs are updated daily. Unless it's a serverless template that caches the version number for 24h and we agree that liquidjs version number might be a patch version or two out of sync briefly.

Or how would the docs know if I'm on 0.12.1/latest vs a Canary build?

cfjedimaster commented 2 years ago

I'd assume that the docs would always talk about the current version. So if Eleventy 1.0 ships with Liquid 10, and Elevent 1.X to 1.9 never changes that, then there's no need to edit the doc.

Basically - the assumption is the current version of Eleventy.

pdehaan commented 2 years ago

Yeah, that makes sense. I was just imagining the cases where the docs mention upcoming 1.0 features (https://www.11ty.dev/docs/data-global-custom/), but that says "Coming soon in v1.0.0". But I agree that it's probably best to just target the stable/latest version of 11ty and if somebody is running a beta or canary build, they can peek at the repos package.json file or something to get a semver range, if needed.

cfjedimaster commented 2 years ago

Heh, well, imo, the docs shouldn't have that. To me it's confusing and should be kept to blog posts, or a dedicated "Coming Soon" page. As a reader, don't make me think. if I see, "this is a feature", I should always assume it's the released version. But - that's another issue and I figure I'm in the minority there. ;)