fix docs.rs show features (#209)

* use crates.io to get deps info

* fix empty features

* update permissions, show features, remove tests/script-lib.spec.js
This commit is contained in:
Light Ning 2022-11-14 23:30:39 +08:00 committed by GitHub
parent a73754bba8
commit b578fd882a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 31 additions and 104 deletions

View file

@ -114,10 +114,14 @@ document.addEventListener("DOMContentLoaded", async () => {
}); });
async function enhanceFeatureFlagsMenu(menu) { async function enhanceFeatureFlagsMenu(menu) {
if (crateVersion === 'latest') {
crateVersion = parseCrateVersionFromDOM();
}
// Use rawCrateName to fetch the Cargo.toml, otherwise will get 404. // Use rawCrateName to fetch the Cargo.toml, otherwise will get 404.
let cargoTomUrl = `https://docs.rs/crate/${rawCrateName}/${crateVersion}/source/Cargo.toml`; let cargoTomUrl = `https://docs.rs/crate/${rawCrateName}/${crateVersion}/source/Cargo.toml`;
let response = await fetch(cargoTomUrl); let crateAPIURL = `https://crates.io/api/v1/crates/${rawCrateName}/${crateVersion}`;
let content = await response.text(); let response = await fetch(crateAPIURL);
let content = await response.json();
let features = parseCargoFeatures(content); let features = parseCargoFeatures(content);
let html = `<div style="padding: 1rem"><p> let html = `<div style="padding: 1rem"><p>
This crate has no explicit-declared feature flag. This crate has no explicit-declared feature flag.
@ -141,7 +145,10 @@ async function enhanceFeatureFlagsMenu(menu) {
} }
// Render optional dependency list. // Render optional dependency list.
let optionalDependencies = parseOptionalDependencies(content); let depsURL = `https://crates.io/api/v1/crates/${rawCrateName}/${crateVersion}/dependencies`;
let depsResponse = await fetch(depsURL);
let depsContent = await depsResponse.json();
let optionalDependencies = parseOptionalDependencies(depsContent);
let dependeciesList = optionalDependencies.map(dependency => ` let dependeciesList = optionalDependencies.map(dependency => `
<li class="optional-dependency-item"> <li class="optional-dependency-item">
<a href="https://docs.rs/${dependency}"> <a href="https://docs.rs/${dependency}">
@ -249,4 +256,4 @@ window.addEventListener("message", async function (event) {
insertAddToExtensionElement(getState(message.crateVersion)); insertAddToExtensionElement(getState(message.crateVersion));
console.log("Congrats! This crate has been installed successfully!"); console.log("Congrats! This crate has been installed successfully!");
} }
}); });

View file

@ -4,7 +4,7 @@ function isRustDoc() {
return gen && gen.getAttribute('content') === 'rustdoc'; return gen && gen.getAttribute('content') === 'rustdoc';
} }
// Since this PR (https://github.com/rust-lang/docs.rs/pull/1527) merged, // Since this PR (https://github.com/rust-lang/docs.rs/pull/1527) merged,
// the latest version path has changed: // the latest version path has changed:
// from https://docs.rs/tokio/1.14.0/tokio/ to https://docs.rs/tokio/latest/tokio/ // from https://docs.rs/tokio/1.14.0/tokio/ to https://docs.rs/tokio/latest/tokio/
// //
@ -13,7 +13,7 @@ function isRustDoc() {
function parseCrateVersionFromDOM() { function parseCrateVersionFromDOM() {
let versionText = document.querySelector('form .crate-name>.title').textContent; let versionText = document.querySelector('form .crate-name>.title').textContent;
if (versionText) { if (versionText) {
// The form of versionText is {crateName}-{version}, separated by hypen, e.g. 'tokio-1.7.0', // The form of versionText is {crateName}-{version}, separated by hypen, e.g. 'tokio-1.7.0',
// However, the crate name could contains hypen too, such as 'tracing-subscriber-0.3.9'. // However, the crate name could contains hypen too, such as 'tracing-subscriber-0.3.9'.
let lastHypenIndex = versionText.lastIndexOf('-'); let lastHypenIndex = versionText.lastIndexOf('-');
return versionText.substring(lastHypenIndex + 1); return versionText.substring(lastHypenIndex + 1);
@ -23,19 +23,20 @@ function parseCrateVersionFromDOM() {
} }
function parseCargoFeatures(content) { function parseCargoFeatures(content) {
if (!content.version?.features) {
return [];
}
let features = []; let features = [];
let start = content.lastIndexOf("[features]"); function to_string(flags) {
if (start !== -1) { return "[" + flags.map(i => '"' + i.toString() + '"').join(', ') + "]"
let lines = content.slice(start + "[features]\n".length).split("\n"); }
for (let line of lines) {
if (/.* = \[.*]/g.test(line)) { for (const [name, flags] of Object.entries(content.version.features)) {
let [name, flags] = line.split("="); if (name === "default") {
flags = flags.trim().replace(/"/ig, ""); features.unshift([name, to_string(flags)]);
features.push([name.trim(), flags]); } else {
} else { features.push([name, to_string(flags)]);
break;
}
} }
} }
return features; return features;
@ -43,29 +44,17 @@ function parseCargoFeatures(content) {
/** /**
* Parse optional dependecies from Cargo.tom HTML page. * Parse optional dependecies from Cargo.tom HTML page.
* *
* @param {*} content HTML page of Cargo.toml content * @param {*} content HTML page of Cargo.toml content
* @returns the list of optional dependencies * @returns the list of optional dependencies
*/ */
function parseOptionalDependencies(content) { function parseOptionalDependencies(content) {
let dependencies = []; let dependencies = [];
let start = content.indexOf("[dependencies."); for (let dep of content.dependencies) {
if (start !== -1) { if (dep.optional) {
let lines = content.slice(start).split("\n"); dependencies.push(dep.crate_id);
let currentCrate = null;
for (let line of lines) {
let match = line.match(/\[dependencies\.(.+)]/);
if (match) {
currentCrate = match[1];
} else if (currentCrate && /optional = true/g.test(line)) {
dependencies.push(currentCrate);
currentCrate = null;
} else if (line.startsWith("[dev-dependencies.")) {
break;
}
} }
} }
return dependencies; return dependencies;
} }
@ -81,4 +70,4 @@ function injectScripts(paths) {
}).forEach(script => { }).forEach(script => {
document.body.insertAdjacentElement('beforeBegin', script); document.body.insertAdjacentElement('beforeBegin', script);
}); });
} }

View file

@ -46,7 +46,7 @@ else
local INDEX_MANAGER_FILES = ['core/storage.js', 'index-manager.js']; local INDEX_MANAGER_FILES = ['core/storage.js', 'index-manager.js'];
json.addIcons(icons()) json.addIcons(icons())
.addPermissions(['storage', 'unlimitedStorage']) .addPermissions(['storage', 'unlimitedStorage', '*://crates.io/api/v1/crates/*'])
.setOptionsUi('manage/index.html') .setOptionsUi('manage/index.html')
.addContentScript( .addContentScript(
matches=['*://docs.rs/*'], matches=['*://docs.rs/*'],

View file

@ -1,69 +0,0 @@
describe("Script library function", function() {
describe(".parseCargoFeatures()", async function() {
let inputs = [
[`<div><pre><code>
[features]
rt = [&quot;stm32f30x-hal/rt&quot;]
</code></pre></div>`,
[["rt", "[&quot;stm32f30x-hal/rt&quot;]"]]
],
[`<div><pre><code>
[features]
rt = [&quot;stm32f30x-hal/rt&quot;]
</code></pre></div>`,
[["rt", "[&quot;stm32f30x-hal/rt&quot;]"]]
],
[`<div><pre><code>
</code></pre></div>`,
[]
],
[`<div><pre><code>
[features]
</code></pre></div>`,
[]
],
[`<div><pre><code>
[package]
authors = ""
</code></pre></div>`,
[]
],
[`<div><pre><code>
[features]
tls = [&quot;sqlx-core/tls&quot;]
uuid = [&quot;sqlx-core/uuid&quot;, &quot;sqlx-macros/uuid&quot;]
</code></pre></div>`,
[["tls", "[&quot;sqlx-core/tls&quot;]"], ["uuid", "[&quot;sqlx-core/uuid&quot;, &quot;sqlx-macros/uuid&quot;]"]]
],
[`<div><pre><code>
[features]
tls = [&quot;sqlx-core/tls&quot;]
uuid = [&quot;sqlx-core/uuid&quot;, &quot;sqlx-macros/uuid&quot;]
[package]
authors = []
</code></pre></div>`,
[["tls", "[&quot;sqlx-core/tls&quot;]"], ["uuid", "[&quot;sqlx-core/uuid&quot;, &quot;sqlx-macros/uuid&quot;]"]]
],
[`<div><pre><code>
[features]
tls = [&quot;sqlx-core/tls&quot;]
uuid = [&quot;sqlx-core/uuid&quot;, &quot;sqlx-macros/uuid&quot;]
default = []
[package]
authors = ""
</code></pre></div>`,
[["tls", "[&quot;sqlx-core/tls&quot;]"], ["uuid", "[&quot;sqlx-core/uuid&quot;, &quot;sqlx-macros/uuid&quot;]"], ["default", "[]"]]
],
];
inputs.forEach(([content, expected], number) => {
it(`Parse crate feature flags #${number}`, function() {
let features = parseCargoFeatures(content);
features.should.has.lengthOf(expected.length);
features.should.deep.equal(expected);
});
});
});
});