156 lines
4.5 KiB
JavaScript
156 lines
4.5 KiB
JavaScript
|
const fs = require("fs");
|
||
|
const path = require("path");
|
||
|
|
||
|
const createPackageURL = require("../utils/createPackageURL");
|
||
|
const createSearch = require("./utils/createSearch");
|
||
|
const incrementCounter = require("./utils/incrementCounter");
|
||
|
|
||
|
/**
|
||
|
* File extensions to look for when automatically resolving.
|
||
|
*/
|
||
|
const resolveExtensions = ["", ".js", ".json"];
|
||
|
|
||
|
/**
|
||
|
* Resolves a path like "lib/file" into "lib/file.js" or "lib/file.json"
|
||
|
* depending on which one is available, similar to require('lib/file').
|
||
|
*/
|
||
|
function resolveFile(base, useIndex, callback) {
|
||
|
resolveExtensions.reduceRight((next, ext) => {
|
||
|
const file = base + ext;
|
||
|
|
||
|
return () => {
|
||
|
fs.stat(file, (error, stats) => {
|
||
|
if (error) {
|
||
|
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
||
|
next();
|
||
|
} else {
|
||
|
callback(error);
|
||
|
}
|
||
|
} else if (useIndex && stats.isDirectory()) {
|
||
|
resolveFile(
|
||
|
path.join(file, "index"),
|
||
|
false,
|
||
|
(error, indexFile, indexStats) => {
|
||
|
if (error) {
|
||
|
callback(error);
|
||
|
} else if (indexFile) {
|
||
|
callback(null, indexFile, indexStats);
|
||
|
} else {
|
||
|
next();
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
callback(null, file, stats);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
}, callback)();
|
||
|
}
|
||
|
|
||
|
function getBasename(file) {
|
||
|
return path.basename(file, path.extname(file));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find the file targeted by the request and get its stats. Redirect
|
||
|
* inexact paths in ?module mode so relative imports resolve correctly.
|
||
|
*/
|
||
|
function findFile(req, res, next) {
|
||
|
let filename = req.filename;
|
||
|
let useIndex = true;
|
||
|
|
||
|
if (req.query.module != null) {
|
||
|
// They want an ES module.
|
||
|
if (!filename) {
|
||
|
// See https://github.com/rollup/rollup/wiki/pkg.module
|
||
|
filename =
|
||
|
req.packageConfig.module || req.packageConfig["jsnext:main"] || "/";
|
||
|
}
|
||
|
} else if (filename) {
|
||
|
// They are requesting an explicit filename. Only try to find an
|
||
|
// index.js if they are NOT requesting an HTML directory listing.
|
||
|
useIndex = filename.charAt(filename.length - 1) !== "/";
|
||
|
} else if (
|
||
|
req.query.main &&
|
||
|
typeof req.packageConfig[req.query.main] === "string"
|
||
|
) {
|
||
|
// They specified a custom ?main field.
|
||
|
// Deprecated, see https://github.com/unpkg/unpkg/issues/63
|
||
|
filename = req.packageConfig[req.query.main];
|
||
|
|
||
|
// Count which packages are using this so we can warn them when we
|
||
|
// remove this functionality.
|
||
|
incrementCounter(
|
||
|
"package-json-custom-main",
|
||
|
req.packageSpec + "?main=" + req.query.main,
|
||
|
1
|
||
|
);
|
||
|
} else if (typeof req.packageConfig.unpkg === "string") {
|
||
|
// The "unpkg" field allows packages to explicitly declare the
|
||
|
// file to serve at the bare URL.
|
||
|
filename = req.packageConfig.unpkg;
|
||
|
} else if (typeof req.packageConfig.browser === "string") {
|
||
|
// Fall back to the "browser" field if declared (only support strings).
|
||
|
// Deprecated, see https://github.com/unpkg/unpkg/issues/63
|
||
|
filename = req.packageConfig.browser;
|
||
|
|
||
|
// Count which packages + versions are actually using this fallback
|
||
|
// so we can warn them when we deprecate this functionality.
|
||
|
incrementCounter("package-json-browser-fallback", req.packageSpec, 1);
|
||
|
} else {
|
||
|
// Fall back to "main" or / (same as npm).
|
||
|
filename = req.packageConfig.main || "/";
|
||
|
}
|
||
|
|
||
|
resolveFile(
|
||
|
path.join(req.packageDir, filename),
|
||
|
useIndex,
|
||
|
(error, file, stats) => {
|
||
|
if (error) console.error(error);
|
||
|
|
||
|
if (file == null) {
|
||
|
return res
|
||
|
.status(404)
|
||
|
.type("text")
|
||
|
.send(
|
||
|
`Cannot find module "${filename}" in package ${req.packageSpec}`
|
||
|
);
|
||
|
}
|
||
|
|
||
|
filename = file.replace(req.packageDir, "");
|
||
|
|
||
|
if (
|
||
|
req.query.main != null ||
|
||
|
getBasename(req.filename) !== getBasename(filename)
|
||
|
) {
|
||
|
// Need to redirect to the module file so relative imports resolve
|
||
|
// correctly.
|
||
|
delete req.query.main;
|
||
|
|
||
|
// Cache module redirects for 1 minute.
|
||
|
res
|
||
|
.set({
|
||
|
"Cache-Control": "public, max-age=60",
|
||
|
"Cache-Tag": "redirect,module-redirect"
|
||
|
})
|
||
|
.redirect(
|
||
|
302,
|
||
|
createPackageURL(
|
||
|
req.packageName,
|
||
|
req.packageVersion,
|
||
|
filename,
|
||
|
createSearch(req.query)
|
||
|
)
|
||
|
);
|
||
|
} else {
|
||
|
req.filename = filename;
|
||
|
req.stats = stats;
|
||
|
next();
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
module.exports = findFile;
|