up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Symbols Server CI / symbols-smoke (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Symbols Server CI / symbols-smoke (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
This commit is contained in:
@@ -625,7 +625,7 @@ internal static class NodePackageCollector
|
||||
var lifecycleScripts = ExtractLifecycleScripts(root);
|
||||
var nodeVersions = NodeVersionDetector.Detect(context, relativeDirectory, cancellationToken);
|
||||
|
||||
return new NodePackage(
|
||||
var package = new NodePackage(
|
||||
name: name.Trim(),
|
||||
version: version.Trim(),
|
||||
relativePath: relativeDirectory,
|
||||
@@ -644,6 +644,10 @@ internal static class NodePackageCollector
|
||||
lockLocator: lockLocator,
|
||||
packageSha256: packageSha256,
|
||||
isYarnPnp: yarnPnpPresent);
|
||||
|
||||
AttachEntrypoints(package, root, relativeDirectory);
|
||||
|
||||
return package;
|
||||
}
|
||||
|
||||
private static string NormalizeRelativeDirectory(LanguageAnalyzerContext context, string directory)
|
||||
@@ -825,4 +829,169 @@ internal static class NodePackageCollector
|
||||
=> name.Equals("preinstall", StringComparison.OrdinalIgnoreCase)
|
||||
|| name.Equals("install", StringComparison.OrdinalIgnoreCase)
|
||||
|| name.Equals("postinstall", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private static void AttachEntrypoints(LanguageAnalyzerContext context, NodePackage package, JsonElement root, string relativeDirectory)
|
||||
{
|
||||
static string NormalizePath(string relativeDirectory, string? path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var normalized = path.Replace('\\', '/').Trim();
|
||||
while (normalized.StartsWith("./", StringComparison.Ordinal))
|
||||
{
|
||||
normalized = normalized[2..];
|
||||
}
|
||||
|
||||
normalized = normalized.TrimStart('/');
|
||||
if (string.IsNullOrWhiteSpace(relativeDirectory))
|
||||
{
|
||||
return normalized;
|
||||
}
|
||||
|
||||
return $"{relativeDirectory.TrimEnd('/')}/{normalized}";
|
||||
}
|
||||
|
||||
void AddEntrypoint(string? path, string conditionSet, string? binName = null, string? mainField = null, string? moduleField = null)
|
||||
{
|
||||
var normalized = NormalizePath(relativeDirectory, path);
|
||||
if (string.IsNullOrWhiteSpace(normalized))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
package.AddEntrypoint(normalized, conditionSet, binName, mainField, moduleField);
|
||||
}
|
||||
|
||||
if (root.TryGetProperty("bin", out var binElement))
|
||||
{
|
||||
if (binElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
AddEntrypoint(binElement.GetString(), string.Empty, binName: null);
|
||||
}
|
||||
else if (binElement.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
foreach (var prop in binElement.EnumerateObject())
|
||||
{
|
||||
if (prop.Value.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
AddEntrypoint(prop.Value.GetString(), string.Empty, binName: prop.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root.TryGetProperty("main", out var mainElement) && mainElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var mainField = mainElement.GetString();
|
||||
AddEntrypoint(mainField, string.Empty, mainField: mainField);
|
||||
}
|
||||
|
||||
if (root.TryGetProperty("module", out var moduleElement) && moduleElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var moduleField = moduleElement.GetString();
|
||||
AddEntrypoint(moduleField, string.Empty, moduleField: moduleField);
|
||||
}
|
||||
|
||||
if (root.TryGetProperty("exports", out var exportsElement))
|
||||
{
|
||||
foreach (var export in FlattenExports(exportsElement, prefix: string.Empty))
|
||||
{
|
||||
AddEntrypoint(export.Path, export.Conditions, binName: null, mainField: null, moduleField: null);
|
||||
}
|
||||
}
|
||||
|
||||
DetectShebangEntrypoints(context, package, relativeDirectory);
|
||||
}
|
||||
|
||||
private static IEnumerable<(string Path, string Conditions)> FlattenExports(JsonElement element, string prefix)
|
||||
{
|
||||
switch (element.ValueKind)
|
||||
{
|
||||
case JsonValueKind.String:
|
||||
var value = element.GetString();
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
yield return (value!, prefix);
|
||||
}
|
||||
yield break;
|
||||
|
||||
case JsonValueKind.Object:
|
||||
foreach (var property in element.EnumerateObject())
|
||||
{
|
||||
var nextPrefix = string.IsNullOrWhiteSpace(prefix) ? property.Name : $"{prefix},{property.Name}";
|
||||
foreach (var nested in FlattenExports(property.Value, nextPrefix))
|
||||
{
|
||||
yield return nested;
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
|
||||
default:
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DetectShebangEntrypoints(LanguageAnalyzerContext context, NodePackage package, string relativeDirectory)
|
||||
{
|
||||
var baseDirectory = string.IsNullOrWhiteSpace(relativeDirectory)
|
||||
? context.RootPath
|
||||
: Path.Combine(context.RootPath, relativeDirectory.Replace('/', Path.DirectorySeparatorChar));
|
||||
|
||||
if (!Directory.Exists(baseDirectory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var candidates = Directory.EnumerateFiles(
|
||||
baseDirectory,
|
||||
"*.*",
|
||||
new EnumerationOptions
|
||||
{
|
||||
RecurseSubdirectories = false,
|
||||
MatchCasing = MatchCasing.CaseInsensitive,
|
||||
IgnoreInaccessible = true
|
||||
})
|
||||
.Where(path =>
|
||||
{
|
||||
var ext = Path.GetExtension(path);
|
||||
return string.Equals(ext, ".js", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(ext, ".mjs", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(ext, ".cjs", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase);
|
||||
})
|
||||
.OrderBy(static p => p, StringComparer.Ordinal);
|
||||
|
||||
foreach (var file in candidates)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var reader = File.OpenText(file);
|
||||
var firstLine = reader.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(firstLine))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!firstLine.TrimStart().StartsWith("#!", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!firstLine.Contains("node", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var relativePath = context.GetRelativePath(file).Replace(Path.DirectorySeparatorChar, '/');
|
||||
package.AddEntrypoint(relativePath, conditionSet: "shebang:node", binName: null, mainField: null, moduleField: null);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// ignore unreadable files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user