piral-cli
Version:
The standard CLI for creating and building a Piral instance or a Pilet.
258 lines • 11.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readImportmap = readImportmap;
const path_1 = require("path");
const log_1 = require("./log");
const version_1 = require("./version");
const hash_1 = require("./hash");
const io_1 = require("./io");
const npm_1 = require("./npm");
const shorthandsUrls = ['', '.', '...'];
function addLocalDependencies(dependencies, realIdentifier, identifier, version, requireVersion, entry, assetName, isAsync) {
const alias = realIdentifier !== identifier ? realIdentifier : undefined;
dependencies.push({
id: `${identifier}@${version}`,
requireId: `${identifier}@${requireVersion}`,
entry,
name: identifier,
ref: `${assetName}.js`,
type: 'local',
alias,
isAsync,
});
}
function getAnyPatch(version) {
const [major, minor] = version.split('.');
return `~${major}.${minor}.0`;
}
function getMatchMajor(version) {
const [major] = version.split('.');
return `^${major}.0.0`;
}
function makeAssetName(id) {
return (id.startsWith('@') ? id.substring(1) : id).replace(/[\/\.]/g, '-').replace(/(\-)+/, '-');
}
function getDependencyDetails(depName, availableSpecs) {
const name = depName.replace(/\?+$/, '');
const isAsync = depName.endsWith('?');
const sep = name.indexOf('@', 1);
if (sep > 0) {
const id = name.substring(0, sep);
const version = name.substring(sep + 1);
const assetName = makeAssetName(id);
return [assetName, id, version, isAsync];
}
else {
const version = availableSpecs[name] || '';
const assetName = makeAssetName(name);
return [assetName, name, version, isAsync];
}
}
async function getLocalDependencyVersion(packageJson, depName, versionSpec, versionBehavior) {
const packageDir = (0, path_1.dirname)(packageJson);
const packageFile = (0, path_1.basename)(packageJson);
const details = await (0, io_1.readJson)(packageDir, packageFile);
if (versionSpec) {
if (!(0, version_1.validate)(versionSpec)) {
(0, log_1.fail)('importMapVersionSpecInvalid_0026', depName);
}
if (!(0, version_1.satisfies)(details.version, versionSpec)) {
(0, log_1.fail)('importMapVersionSpecNotSatisfied_0025', depName, details.version, versionSpec);
}
return [details.name, details.version, versionSpec];
}
switch (versionBehavior) {
case 'all':
return [details.name, details.version, '*'];
case 'match-major':
return [details.name, details.version, getMatchMajor(details.version)];
case 'any-patch':
return [details.name, details.version, getAnyPatch(details.version)];
case 'exact':
default:
return [details.name, details.version, details.version];
}
}
async function getInheritedDependencies(inheritedImport, dir, excludedDependencies, inheritanceBehavior) {
const packageJson = (0, npm_1.tryResolvePackage)(`${inheritedImport}/package.json`, dir);
if (packageJson) {
const packageDir = (0, path_1.dirname)(packageJson);
const packageDetails = await (0, io_1.readJson)(packageDir, 'package.json');
return await consumeImportmap(packageDir, packageDetails, true, 'exact', inheritanceBehavior, excludedDependencies);
}
else {
const directImportmap = (0, npm_1.tryResolvePackage)(inheritedImport, dir);
if (directImportmap) {
const baseDir = (0, path_1.dirname)(directImportmap);
const content = await (0, io_1.readJson)(baseDir, (0, path_1.basename)(directImportmap));
return await resolveImportmap(baseDir, content, {
availableSpecs: {},
inheritanceBehavior,
excludedDependencies,
ignoreFailure: true,
versionBehavior: 'exact',
});
}
}
return [];
}
async function resolveImportmap(dir, importmap, options) {
const dependencies = [];
const sharedImports = importmap?.imports;
const inheritedImports = importmap?.inherit;
const excludedImports = importmap?.exclude;
const onUnresolved = (name, version) => {
if (options.ignoreFailure) {
const id = version ? `${name}@${version}` : name;
(0, log_1.log)('skipUnresolvedDependency_0054', id);
}
else {
(0, log_1.fail)('importMapReferenceNotFound_0027', dir, name);
}
};
if (typeof sharedImports === 'object' && sharedImports) {
for (const depName of Object.keys(sharedImports)) {
const url = sharedImports[depName];
const [assetName, identifier, versionSpec, isAsync] = getDependencyDetails(depName, options.availableSpecs);
if (options.excludedDependencies.includes(identifier)) {
continue;
}
else if (typeof url !== 'string') {
(0, log_1.log)('generalInfo_0000', `The value of "${depName}" in the importmap is not a string and will be ignored.`);
}
else if (/^https?:\/\//.test(url)) {
const hash = (0, hash_1.computeHash)(url).substring(0, 7);
dependencies.push({
id: `${identifier}@${hash}`,
requireId: `${identifier}@${hash}`,
entry: url,
name: identifier,
ref: url,
type: 'remote',
isAsync,
});
}
else if (url === identifier || shorthandsUrls.includes(url)) {
const entry = (0, npm_1.tryResolvePackage)(identifier, dir);
if (entry) {
const packageJson = await (0, io_1.findFile)((0, path_1.dirname)(entry), 'package.json');
const [realIdentifier, version, requireVersion] = await getLocalDependencyVersion(packageJson, depName, versionSpec, options.versionBehavior);
addLocalDependencies(dependencies, realIdentifier, identifier, version, requireVersion, entry, assetName, isAsync);
}
else {
onUnresolved(identifier, versionSpec);
}
}
else if (!url.startsWith('.') && !(0, path_1.isAbsolute)(url)) {
const entry = (0, npm_1.tryResolvePackage)(url, dir);
if (entry) {
const packageJson = await (0, io_1.findFile)((0, path_1.dirname)(entry), 'package.json');
const [realIdentifier, version, requireVersion] = await getLocalDependencyVersion(packageJson, depName, versionSpec, options.versionBehavior);
addLocalDependencies(dependencies, realIdentifier, identifier, version, requireVersion, entry, assetName, isAsync);
}
else {
onUnresolved(url, versionSpec);
}
}
else {
const entry = (0, path_1.resolve)(dir, url);
const exists = await (0, io_1.checkExists)(entry);
if (exists) {
const isDirectory = await (0, io_1.checkIsDirectory)(entry);
const packageJson = isDirectory
? (0, path_1.resolve)(entry, 'package.json')
: await (0, io_1.findFile)((0, path_1.dirname)(entry), 'package.json');
const packageJsonExists = await (0, io_1.checkExists)(packageJson);
if (packageJsonExists) {
const [realIdentifier, version, requireVersion] = await getLocalDependencyVersion(packageJson, depName, versionSpec, options.versionBehavior);
addLocalDependencies(dependencies, realIdentifier, identifier, version, requireVersion, isDirectory ? (0, npm_1.tryResolvePackage)(entry, dir) : entry, assetName, isAsync);
}
else if (isDirectory) {
onUnresolved(entry, versionSpec);
}
else {
const hash = await (0, io_1.getHash)(entry);
dependencies.push({
id: `${identifier}@${hash}`,
requireId: `${identifier}@${hash}`,
entry,
name: identifier,
ref: `${assetName}.js`,
type: 'local',
isAsync,
});
}
}
else {
onUnresolved(url, versionSpec);
}
}
}
}
if (Array.isArray(inheritedImports)) {
const includedImports = [...options.excludedDependencies, ...dependencies.map((m) => m.name)];
const excluded = Array.isArray(excludedImports) ? [...includedImports, ...excludedImports] : includedImports;
for (const inheritedImport of inheritedImports) {
const otherDependencies = await getInheritedDependencies(inheritedImport, dir, excluded, options.inheritanceBehavior);
for (const dependency of otherDependencies) {
const entry = dependencies.find((dep) => dep.name === dependency.name);
if (!entry) {
dependencies.push({
...dependency,
parents: [inheritedImport],
});
}
else if (Array.isArray(entry.parents)) {
entry.parents.push(inheritedImport);
}
}
}
}
return dependencies;
}
async function consumeImportmap(dir, packageDetails, inherited, versionBehavior, mode, excludedDependencies) {
const importmap = packageDetails.importmap;
const appShell = inherited && mode === 'remote';
const availableSpecs = appShell ? packageDetails.devDependencies ?? {} : {};
const inheritanceBehavior = appShell ? 'host' : mode;
if (typeof importmap === 'string') {
const notFound = {};
const content = await (0, io_1.readJson)(dir, importmap, notFound);
if (content === notFound) {
(0, log_1.fail)('importMapFileNotFound_0028', dir, importmap);
}
const baseDir = (0, path_1.dirname)((0, path_1.resolve)(dir, importmap));
return await resolveImportmap(baseDir, content, {
availableSpecs,
ignoreFailure: inherited,
excludedDependencies,
versionBehavior,
inheritanceBehavior,
});
}
else if (typeof importmap === 'undefined' && inherited) {
// Fall back to sharedDependencies or pilets.external if available
const shared = packageDetails.sharedDependencies ?? packageDetails.pilets?.externals;
if (Array.isArray(shared)) {
return shared.map((dep) => ({
id: dep,
name: dep,
entry: dep,
type: 'local',
ref: undefined,
requireId: dep,
}));
}
}
return await resolveImportmap(dir, importmap, {
availableSpecs,
excludedDependencies,
ignoreFailure: inherited,
versionBehavior,
inheritanceBehavior,
});
}
function readImportmap(dir, packageDetails, versionBehavior = 'exact', inheritanceBehavior = 'remote') {
return consumeImportmap(dir, packageDetails, false, versionBehavior, inheritanceBehavior, []);
}
//# sourceMappingURL=importmap.js.map