@topogram/cli 0.3.42 → 0.3.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli.js +367 -12
- package/src/new-project.js +3 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -219,6 +219,7 @@ function printUsage(options = {}) {
|
|
|
219
219
|
console.log(" or: topogram generator show <id-or-package> [--json]");
|
|
220
220
|
console.log(" or: topogram generator check <path-or-package> [--json]");
|
|
221
221
|
console.log(" or: topogram generator policy init [path] [--json]");
|
|
222
|
+
console.log(" or: topogram generator policy status [path] [--json]");
|
|
222
223
|
console.log(" or: topogram generator policy check [path] [--json]");
|
|
223
224
|
console.log(" or: topogram generator policy explain [path] [--json]");
|
|
224
225
|
console.log(" or: topogram generator policy pin [package@version] [path] [--json]");
|
|
@@ -586,6 +587,7 @@ function printGeneratorHelp() {
|
|
|
586
587
|
console.log(" or: topogram generator show <id-or-package> [--json]");
|
|
587
588
|
console.log(" or: topogram generator check <path-or-package> [--json]");
|
|
588
589
|
console.log(" or: topogram generator policy init [path] [--json]");
|
|
590
|
+
console.log(" or: topogram generator policy status [path] [--json]");
|
|
589
591
|
console.log(" or: topogram generator policy check [path] [--json]");
|
|
590
592
|
console.log(" or: topogram generator policy explain [path] [--json]");
|
|
591
593
|
console.log(" or: topogram generator policy pin [package@version] [path] [--json]");
|
|
@@ -607,6 +609,7 @@ function printGeneratorHelp() {
|
|
|
607
609
|
console.log(" topogram generator check ./generator-package");
|
|
608
610
|
console.log(" topogram generator check @scope/topogram-generator-web --json");
|
|
609
611
|
console.log(" topogram generator policy init");
|
|
612
|
+
console.log(" topogram generator policy status --json");
|
|
610
613
|
console.log(" topogram generator policy check --json");
|
|
611
614
|
console.log(" topogram generator policy pin @topogram/generator-react-web@1");
|
|
612
615
|
}
|
|
@@ -1586,9 +1589,270 @@ function effectiveGeneratorPolicy(policyInfo) {
|
|
|
1586
1589
|
};
|
|
1587
1590
|
}
|
|
1588
1591
|
|
|
1592
|
+
/**
|
|
1593
|
+
* @param {string} filePath
|
|
1594
|
+
* @returns {any|null}
|
|
1595
|
+
*/
|
|
1596
|
+
function readJsonIfPresent(filePath) {
|
|
1597
|
+
if (!fs.existsSync(filePath)) {
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
try {
|
|
1601
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
1602
|
+
} catch {
|
|
1603
|
+
return null;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
/**
|
|
1608
|
+
* @param {Record<string, any>|null} projectPackage
|
|
1609
|
+
* @param {string} packageName
|
|
1610
|
+
* @returns {{ field: string|null, spec: string|null }}
|
|
1611
|
+
*/
|
|
1612
|
+
function dependencySpecForPackage(projectPackage, packageName) {
|
|
1613
|
+
const dependencyFields = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
|
1614
|
+
for (const field of dependencyFields) {
|
|
1615
|
+
const dependencies = projectPackage?.[field];
|
|
1616
|
+
if (dependencies && typeof dependencies === "object" && typeof dependencies[packageName] === "string") {
|
|
1617
|
+
return {
|
|
1618
|
+
field,
|
|
1619
|
+
spec: dependencies[packageName]
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
return {
|
|
1624
|
+
field: null,
|
|
1625
|
+
spec: null
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/**
|
|
1630
|
+
* @param {Record<string, any>|null} lockfile
|
|
1631
|
+
* @param {string} packageName
|
|
1632
|
+
* @returns {{ version: string|null, resolved: string|null, integrity: string|null, entryPath: string|null }}
|
|
1633
|
+
*/
|
|
1634
|
+
function npmLockfileInfoForPackage(lockfile, packageName) {
|
|
1635
|
+
const packageEntryPath = `node_modules/${packageName}`;
|
|
1636
|
+
const packageEntry = lockfile?.packages?.[packageEntryPath];
|
|
1637
|
+
if (packageEntry && typeof packageEntry === "object") {
|
|
1638
|
+
return {
|
|
1639
|
+
version: typeof packageEntry.version === "string" ? packageEntry.version : null,
|
|
1640
|
+
resolved: typeof packageEntry.resolved === "string" ? packageEntry.resolved : null,
|
|
1641
|
+
integrity: typeof packageEntry.integrity === "string" ? packageEntry.integrity : null,
|
|
1642
|
+
entryPath: packageEntryPath
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
const dependencyEntry = lockfile?.dependencies?.[packageName];
|
|
1646
|
+
if (dependencyEntry && typeof dependencyEntry === "object") {
|
|
1647
|
+
return {
|
|
1648
|
+
version: typeof dependencyEntry.version === "string" ? dependencyEntry.version : null,
|
|
1649
|
+
resolved: typeof dependencyEntry.resolved === "string" ? dependencyEntry.resolved : null,
|
|
1650
|
+
integrity: typeof dependencyEntry.integrity === "string" ? dependencyEntry.integrity : null,
|
|
1651
|
+
entryPath: packageName
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
return {
|
|
1655
|
+
version: null,
|
|
1656
|
+
resolved: null,
|
|
1657
|
+
integrity: null,
|
|
1658
|
+
entryPath: null
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* @param {string} projectRoot
|
|
1664
|
+
* @returns {{ kind: "npm"|"pnpm"|"yarn"|"bun"|null, path: string|null, data: Record<string, any>|null, note: string|null }}
|
|
1665
|
+
*/
|
|
1666
|
+
function lockfileMetadataForProject(projectRoot) {
|
|
1667
|
+
const npmLockfilePath = path.join(projectRoot, "package-lock.json");
|
|
1668
|
+
if (fs.existsSync(npmLockfilePath)) {
|
|
1669
|
+
return {
|
|
1670
|
+
kind: "npm",
|
|
1671
|
+
path: npmLockfilePath,
|
|
1672
|
+
data: readJsonIfPresent(npmLockfilePath),
|
|
1673
|
+
note: null
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
const candidates = [
|
|
1677
|
+
{ kind: "pnpm", file: "pnpm-lock.yaml" },
|
|
1678
|
+
{ kind: "yarn", file: "yarn.lock" },
|
|
1679
|
+
{ kind: "bun", file: "bun.lock" },
|
|
1680
|
+
{ kind: "bun", file: "bun.lockb" }
|
|
1681
|
+
];
|
|
1682
|
+
for (const candidate of candidates) {
|
|
1683
|
+
const lockfilePath = path.join(projectRoot, candidate.file);
|
|
1684
|
+
if (fs.existsSync(lockfilePath)) {
|
|
1685
|
+
return {
|
|
1686
|
+
kind: /** @type {"pnpm"|"yarn"|"bun"} */ (candidate.kind),
|
|
1687
|
+
path: lockfilePath,
|
|
1688
|
+
data: null,
|
|
1689
|
+
note: `${candidate.file} found; package versions are not inspected by this command.`
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return {
|
|
1694
|
+
kind: null,
|
|
1695
|
+
path: null,
|
|
1696
|
+
data: null,
|
|
1697
|
+
note: null
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
/**
|
|
1702
|
+
* @param {string} projectRoot
|
|
1703
|
+
* @param {string} packageName
|
|
1704
|
+
* @returns {{ dependencyField: string|null, dependencySpec: string|null, installedVersion: string|null, installedPackageJsonPath: string|null, lockfileKind: "npm"|"pnpm"|"yarn"|"bun"|null, lockfilePath: string|null, lockfileVersion: string|null, lockfileResolved: string|null, lockfileIntegrity: string|null, lockfileEntryPath: string|null, lockfileNote: string|null }}
|
|
1705
|
+
*/
|
|
1706
|
+
function packageInfoForGenerator(projectRoot, packageName) {
|
|
1707
|
+
const projectPackage = readJsonIfPresent(path.join(projectRoot, "package.json"));
|
|
1708
|
+
const dependency = dependencySpecForPackage(projectPackage, packageName);
|
|
1709
|
+
const lockfile = lockfileMetadataForProject(projectRoot);
|
|
1710
|
+
const lockfileInfo = lockfile.kind === "npm"
|
|
1711
|
+
? npmLockfileInfoForPackage(lockfile.data, packageName)
|
|
1712
|
+
: {
|
|
1713
|
+
version: null,
|
|
1714
|
+
resolved: null,
|
|
1715
|
+
integrity: null,
|
|
1716
|
+
entryPath: null
|
|
1717
|
+
};
|
|
1718
|
+
const installedPackageJsonPath = path.join(projectRoot, "node_modules", ...packageName.split("/"), "package.json");
|
|
1719
|
+
const installedPackage = readJsonIfPresent(installedPackageJsonPath);
|
|
1720
|
+
return {
|
|
1721
|
+
dependencyField: dependency.field,
|
|
1722
|
+
dependencySpec: dependency.spec,
|
|
1723
|
+
installedVersion: typeof installedPackage?.version === "string" ? installedPackage.version : null,
|
|
1724
|
+
installedPackageJsonPath: installedPackage ? installedPackageJsonPath : null,
|
|
1725
|
+
lockfileKind: lockfile.kind,
|
|
1726
|
+
lockfilePath: lockfile.path,
|
|
1727
|
+
lockfileVersion: lockfileInfo.version,
|
|
1728
|
+
lockfileResolved: lockfileInfo.resolved,
|
|
1729
|
+
lockfileIntegrity: lockfileInfo.integrity,
|
|
1730
|
+
lockfileEntryPath: lockfileInfo.entryPath,
|
|
1731
|
+
lockfileNote: lockfile.note
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* @param {ReturnType<typeof packageInfoForGenerator>} packageInfo
|
|
1737
|
+
* @returns {string}
|
|
1738
|
+
*/
|
|
1739
|
+
function formatGeneratorPackageLockfile(packageInfo) {
|
|
1740
|
+
if (!packageInfo.lockfileKind || !packageInfo.lockfilePath) {
|
|
1741
|
+
return "(not found)";
|
|
1742
|
+
}
|
|
1743
|
+
const label = path.basename(packageInfo.lockfilePath);
|
|
1744
|
+
if (packageInfo.lockfileVersion) {
|
|
1745
|
+
return `${packageInfo.lockfileKind} ${packageInfo.lockfileVersion}`;
|
|
1746
|
+
}
|
|
1747
|
+
return `${label} (version not inspected)`;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
/**
|
|
1751
|
+
* @param {string} projectRoot
|
|
1752
|
+
* @param {import("./generator-policy.js").GeneratorPolicy} policy
|
|
1753
|
+
* @param {ReturnType<typeof packageBackedGeneratorBindings>[number]} binding
|
|
1754
|
+
* @returns {ReturnType<typeof packageBackedGeneratorBindings>[number] & { allowed: boolean, packageInfo: ReturnType<typeof packageInfoForGenerator>, pin: { key: string|null, version: string|null, matches: boolean|null } }}
|
|
1755
|
+
*/
|
|
1756
|
+
function generatorPolicyBindingStatus(projectRoot, policy, binding) {
|
|
1757
|
+
const packagePin = policy.pinnedVersions[binding.packageName] || null;
|
|
1758
|
+
const generatorPin = policy.pinnedVersions[binding.generatorId] || null;
|
|
1759
|
+
const pinnedVersion = packagePin || generatorPin;
|
|
1760
|
+
return {
|
|
1761
|
+
...binding,
|
|
1762
|
+
allowed: generatorPackageAllowed(policy, binding.packageName),
|
|
1763
|
+
packageInfo: packageInfoForGenerator(projectRoot, binding.packageName),
|
|
1764
|
+
pin: {
|
|
1765
|
+
key: packagePin ? binding.packageName : generatorPin ? binding.generatorId : null,
|
|
1766
|
+
version: pinnedVersion,
|
|
1767
|
+
matches: pinnedVersion ? pinnedVersion === binding.version : null
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
/**
|
|
1773
|
+
* @param {any[]} diagnostics
|
|
1774
|
+
* @param {Array<ReturnType<typeof generatorPolicyBindingStatus>>} bindings
|
|
1775
|
+
* @returns {any[]}
|
|
1776
|
+
*/
|
|
1777
|
+
function annotateGeneratorPolicyDiagnostics(diagnostics, bindings) {
|
|
1778
|
+
return diagnostics.map((diagnostic) => {
|
|
1779
|
+
const binding = bindings.find((item) => (
|
|
1780
|
+
item.packageName === diagnostic.packageName &&
|
|
1781
|
+
(!diagnostic.componentId || item.componentId === diagnostic.componentId)
|
|
1782
|
+
));
|
|
1783
|
+
if (!binding) {
|
|
1784
|
+
return diagnostic;
|
|
1785
|
+
}
|
|
1786
|
+
return {
|
|
1787
|
+
...diagnostic,
|
|
1788
|
+
packageVersion: binding.packageInfo.installedVersion || binding.packageInfo.lockfileVersion || null,
|
|
1789
|
+
packageDependencyField: binding.packageInfo.dependencyField,
|
|
1790
|
+
packageDependencySpec: binding.packageInfo.dependencySpec,
|
|
1791
|
+
packageLockfileKind: binding.packageInfo.lockfileKind,
|
|
1792
|
+
packageLockfilePath: binding.packageInfo.lockfilePath,
|
|
1793
|
+
packageLockVersion: binding.packageInfo.lockfileVersion
|
|
1794
|
+
};
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* @param {Array<ReturnType<typeof generatorPolicyBindingStatus>>} bindings
|
|
1800
|
+
* @returns {any[]}
|
|
1801
|
+
*/
|
|
1802
|
+
function generatorPolicyPackageMetadataDiagnostics(bindings) {
|
|
1803
|
+
const diagnostics = [];
|
|
1804
|
+
for (const binding of bindings) {
|
|
1805
|
+
if (!binding.packageInfo.dependencySpec) {
|
|
1806
|
+
diagnostics.push({
|
|
1807
|
+
code: "generator_package_dependency_missing",
|
|
1808
|
+
severity: "warning",
|
|
1809
|
+
message: `Component '${binding.componentId}' generator package '${binding.packageName}' is not declared in package.json dependencies.`,
|
|
1810
|
+
path: binding.packageInfo.installedPackageJsonPath,
|
|
1811
|
+
suggestedFix: `Declare '${binding.packageName}' in package.json devDependencies so generator adoption is visible in package review.`,
|
|
1812
|
+
step: "generator-policy",
|
|
1813
|
+
componentId: binding.componentId,
|
|
1814
|
+
generatorId: binding.generatorId,
|
|
1815
|
+
packageName: binding.packageName,
|
|
1816
|
+
version: binding.version,
|
|
1817
|
+
packageVersion: binding.packageInfo.installedVersion || binding.packageInfo.lockfileVersion || null,
|
|
1818
|
+
packageDependencyField: binding.packageInfo.dependencyField,
|
|
1819
|
+
packageDependencySpec: binding.packageInfo.dependencySpec,
|
|
1820
|
+
packageLockfileKind: binding.packageInfo.lockfileKind,
|
|
1821
|
+
packageLockfilePath: binding.packageInfo.lockfilePath,
|
|
1822
|
+
packageLockVersion: binding.packageInfo.lockfileVersion
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
if (
|
|
1826
|
+
binding.packageInfo.installedVersion &&
|
|
1827
|
+
binding.packageInfo.lockfileVersion &&
|
|
1828
|
+
binding.packageInfo.installedVersion !== binding.packageInfo.lockfileVersion
|
|
1829
|
+
) {
|
|
1830
|
+
diagnostics.push({
|
|
1831
|
+
code: "generator_package_version_drift",
|
|
1832
|
+
severity: "warning",
|
|
1833
|
+
message: `Component '${binding.componentId}' generator package '${binding.packageName}' is installed at '${binding.packageInfo.installedVersion}', but package-lock records '${binding.packageInfo.lockfileVersion}'.`,
|
|
1834
|
+
path: binding.packageInfo.lockfilePath,
|
|
1835
|
+
suggestedFix: "Run the package manager install command and review the resulting lockfile before pinning generator policy.",
|
|
1836
|
+
step: "generator-policy",
|
|
1837
|
+
componentId: binding.componentId,
|
|
1838
|
+
generatorId: binding.generatorId,
|
|
1839
|
+
packageName: binding.packageName,
|
|
1840
|
+
version: binding.version,
|
|
1841
|
+
packageVersion: binding.packageInfo.installedVersion,
|
|
1842
|
+
packageDependencyField: binding.packageInfo.dependencyField,
|
|
1843
|
+
packageDependencySpec: binding.packageInfo.dependencySpec,
|
|
1844
|
+
packageLockfileKind: binding.packageInfo.lockfileKind,
|
|
1845
|
+
packageLockfilePath: binding.packageInfo.lockfilePath,
|
|
1846
|
+
packageLockVersion: binding.packageInfo.lockfileVersion
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
return diagnostics;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1589
1853
|
/**
|
|
1590
1854
|
* @param {string} projectPath
|
|
1591
|
-
* @returns {{ ok: boolean, path: string, exists: boolean, policy: any, defaulted: boolean, bindings: ReturnType<typeof
|
|
1855
|
+
* @returns {{ ok: boolean, path: string, exists: boolean, policy: any, defaulted: boolean, bindings: Array<ReturnType<typeof generatorPolicyBindingStatus>>, diagnostics: any[], errors: string[] }}
|
|
1592
1856
|
*/
|
|
1593
1857
|
function buildGeneratorPolicyCheckPayload(projectPath) {
|
|
1594
1858
|
const projectConfigInfo = loadProjectConfig(projectPath);
|
|
@@ -1613,7 +1877,9 @@ function buildGeneratorPolicyCheckPayload(projectPath) {
|
|
|
1613
1877
|
};
|
|
1614
1878
|
}
|
|
1615
1879
|
const policyInfo = loadGeneratorPolicy(projectConfigInfo.configDir);
|
|
1616
|
-
const
|
|
1880
|
+
const rawBindings = packageBackedGeneratorBindings(projectConfigInfo.config);
|
|
1881
|
+
const policy = policyInfo.policy || effectiveGeneratorPolicy(policyInfo);
|
|
1882
|
+
const bindings = rawBindings.map((binding) => generatorPolicyBindingStatus(projectConfigInfo.configDir, policy, binding));
|
|
1617
1883
|
const diagnostics = [];
|
|
1618
1884
|
if (!policyInfo.exists) {
|
|
1619
1885
|
diagnostics.push({
|
|
@@ -1625,16 +1891,18 @@ function buildGeneratorPolicyCheckPayload(projectPath) {
|
|
|
1625
1891
|
step: "generator-policy"
|
|
1626
1892
|
});
|
|
1627
1893
|
}
|
|
1628
|
-
diagnostics.push(...generatorPolicyDiagnosticsForBindings(policyInfo,
|
|
1629
|
-
|
|
1894
|
+
diagnostics.push(...generatorPolicyDiagnosticsForBindings(policyInfo, rawBindings, "generator-policy"));
|
|
1895
|
+
diagnostics.push(...generatorPolicyPackageMetadataDiagnostics(bindings));
|
|
1896
|
+
const annotatedDiagnostics = annotateGeneratorPolicyDiagnostics(diagnostics, bindings);
|
|
1897
|
+
const errors = annotatedDiagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message);
|
|
1630
1898
|
return {
|
|
1631
1899
|
ok: errors.length === 0,
|
|
1632
1900
|
path: policyInfo.path,
|
|
1633
1901
|
exists: policyInfo.exists,
|
|
1634
|
-
policy
|
|
1902
|
+
policy,
|
|
1635
1903
|
defaulted: !policyInfo.exists,
|
|
1636
1904
|
bindings,
|
|
1637
|
-
diagnostics,
|
|
1905
|
+
diagnostics: annotatedDiagnostics,
|
|
1638
1906
|
errors
|
|
1639
1907
|
};
|
|
1640
1908
|
}
|
|
@@ -1686,6 +1954,25 @@ function buildGeneratorPolicyExplainPayload(projectPath) {
|
|
|
1686
1954
|
};
|
|
1687
1955
|
}
|
|
1688
1956
|
|
|
1957
|
+
/**
|
|
1958
|
+
* @param {string} projectPath
|
|
1959
|
+
* @returns {ReturnType<typeof buildGeneratorPolicyExplainPayload> & { summary: { packageBackedGenerators: number, allowed: number, denied: number, pinned: number, unpinned: number, pinMismatches: number } }}
|
|
1960
|
+
*/
|
|
1961
|
+
function buildGeneratorPolicyStatusPayload(projectPath) {
|
|
1962
|
+
const explain = buildGeneratorPolicyExplainPayload(projectPath);
|
|
1963
|
+
return {
|
|
1964
|
+
...explain,
|
|
1965
|
+
summary: {
|
|
1966
|
+
packageBackedGenerators: explain.bindings.length,
|
|
1967
|
+
allowed: explain.bindings.filter((binding) => binding.allowed).length,
|
|
1968
|
+
denied: explain.bindings.filter((binding) => !binding.allowed).length,
|
|
1969
|
+
pinned: explain.bindings.filter((binding) => Boolean(binding.pin.version)).length,
|
|
1970
|
+
unpinned: explain.bindings.filter((binding) => !binding.pin.version).length,
|
|
1971
|
+
pinMismatches: explain.bindings.filter((binding) => binding.pin.matches === false).length
|
|
1972
|
+
}
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1689
1976
|
/**
|
|
1690
1977
|
* @param {ReturnType<typeof buildGeneratorPolicyCheckPayload>} payload
|
|
1691
1978
|
* @returns {void}
|
|
@@ -1698,6 +1985,15 @@ function printGeneratorPolicyCheckPayload(payload) {
|
|
|
1698
1985
|
console.log(`Package-backed generators: ${payload.bindings.length}`);
|
|
1699
1986
|
for (const binding of payload.bindings) {
|
|
1700
1987
|
console.log(`- ${binding.componentId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
1988
|
+
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
1989
|
+
if (binding.packageInfo.dependencySpec) {
|
|
1990
|
+
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
1991
|
+
}
|
|
1992
|
+
if (binding.packageInfo.lockfileVersion) {
|
|
1993
|
+
console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
|
|
1994
|
+
} else if (binding.packageInfo.lockfileKind) {
|
|
1995
|
+
console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
|
|
1996
|
+
}
|
|
1701
1997
|
}
|
|
1702
1998
|
for (const diagnostic of payload.diagnostics) {
|
|
1703
1999
|
console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
|
|
@@ -1710,6 +2006,51 @@ function printGeneratorPolicyCheckPayload(payload) {
|
|
|
1710
2006
|
}
|
|
1711
2007
|
}
|
|
1712
2008
|
|
|
2009
|
+
/**
|
|
2010
|
+
* @param {ReturnType<typeof buildGeneratorPolicyStatusPayload>} payload
|
|
2011
|
+
* @returns {void}
|
|
2012
|
+
*/
|
|
2013
|
+
function printGeneratorPolicyStatusPayload(payload) {
|
|
2014
|
+
console.log(payload.ok ? "Generator policy status: allowed" : "Generator policy status: denied");
|
|
2015
|
+
console.log(`Policy file: ${payload.path}`);
|
|
2016
|
+
console.log(`Policy file exists: ${payload.exists ? "yes" : "no"}`);
|
|
2017
|
+
console.log(`Default policy active: ${payload.defaulted ? "yes" : "no"}`);
|
|
2018
|
+
console.log(`Package-backed generators: ${payload.summary.packageBackedGenerators}`);
|
|
2019
|
+
console.log(`Allowed packages: ${payload.summary.allowed}`);
|
|
2020
|
+
console.log(`Denied packages: ${payload.summary.denied}`);
|
|
2021
|
+
console.log(`Pinned generators: ${payload.summary.pinned}`);
|
|
2022
|
+
console.log(`Unpinned generators: ${payload.summary.unpinned}`);
|
|
2023
|
+
console.log(`Pin mismatches: ${payload.summary.pinMismatches}`);
|
|
2024
|
+
if (payload.bindings.length > 0) {
|
|
2025
|
+
console.log("");
|
|
2026
|
+
console.log("Generator packages:");
|
|
2027
|
+
}
|
|
2028
|
+
for (const binding of payload.bindings) {
|
|
2029
|
+
console.log(`- ${binding.componentId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2030
|
+
console.log(` allowed: ${binding.allowed ? "yes" : "no"}`);
|
|
2031
|
+
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2032
|
+
console.log(` dependency: ${binding.packageInfo.dependencyField && binding.packageInfo.dependencySpec ? `${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}` : "(not declared)"}`);
|
|
2033
|
+
console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
|
|
2034
|
+
console.log(` policy pin: ${binding.pin.version ? `${binding.pin.key}@${binding.pin.version}` : "(none)"}`);
|
|
2035
|
+
}
|
|
2036
|
+
for (const diagnostic of payload.diagnostics) {
|
|
2037
|
+
const label = diagnostic.severity === "warning" ? "Warning" : "Error";
|
|
2038
|
+
console.log(`${label}: ${diagnostic.code}: ${diagnostic.message}`);
|
|
2039
|
+
if (diagnostic.packageVersion) {
|
|
2040
|
+
console.log(` package version: ${diagnostic.packageVersion}`);
|
|
2041
|
+
}
|
|
2042
|
+
if (diagnostic.packageDependencySpec) {
|
|
2043
|
+
console.log(` dependency: ${diagnostic.packageDependencyField} ${diagnostic.packageDependencySpec}`);
|
|
2044
|
+
}
|
|
2045
|
+
if (diagnostic.packageLockfilePath) {
|
|
2046
|
+
console.log(` lockfile: ${path.basename(diagnostic.packageLockfilePath)}${diagnostic.packageLockVersion ? ` ${diagnostic.packageLockVersion}` : ""}`);
|
|
2047
|
+
}
|
|
2048
|
+
if (diagnostic.suggestedFix) {
|
|
2049
|
+
console.log(` fix: ${diagnostic.suggestedFix}`);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
|
|
1713
2054
|
/**
|
|
1714
2055
|
* @param {ReturnType<typeof buildGeneratorPolicyExplainPayload>} payload
|
|
1715
2056
|
* @returns {void}
|
|
@@ -1727,6 +2068,10 @@ function printGeneratorPolicyExplainPayload(payload) {
|
|
|
1727
2068
|
console.log("Package-backed generators:");
|
|
1728
2069
|
for (const binding of payload.bindings) {
|
|
1729
2070
|
console.log(`- ${binding.componentId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2071
|
+
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2072
|
+
if (binding.packageInfo.dependencySpec) {
|
|
2073
|
+
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
2074
|
+
}
|
|
1730
2075
|
}
|
|
1731
2076
|
}
|
|
1732
2077
|
if (payload.rules.length > 0) {
|
|
@@ -1839,10 +2184,6 @@ function buildGeneratorPolicyPinPayload(projectPath, spec) {
|
|
|
1839
2184
|
if (!allowedPackages.includes(pin.packageName)) {
|
|
1840
2185
|
allowedPackages.push(pin.packageName);
|
|
1841
2186
|
}
|
|
1842
|
-
const scope = packageScopeFromName(pin.packageName);
|
|
1843
|
-
if (scope && !allowedPackageScopes.includes(scope)) {
|
|
1844
|
-
allowedPackageScopes.push(scope);
|
|
1845
|
-
}
|
|
1846
2187
|
pinnedVersions[pin.packageName] = pin.version;
|
|
1847
2188
|
}
|
|
1848
2189
|
const nextPolicy = {
|
|
@@ -3715,6 +4056,7 @@ function printNewProjectResult(result, cwd) {
|
|
|
3715
4056
|
console.log(" npm run source:status");
|
|
3716
4057
|
console.log(" npm run template:explain");
|
|
3717
4058
|
console.log(" npm run check");
|
|
4059
|
+
console.log(" npm run generator:policy:status");
|
|
3718
4060
|
console.log(" npm run generator:policy:check");
|
|
3719
4061
|
if (template.includesExecutableImplementation) {
|
|
3720
4062
|
console.log(" npm run template:policy:explain");
|
|
@@ -7219,6 +7561,8 @@ if (args[0] === "version" || args[0] === "--version") {
|
|
|
7219
7561
|
commandArgs = { generatorCheck: true, inputPath: args[2] };
|
|
7220
7562
|
} else if (args[0] === "generator" && args[1] === "policy" && args[2] === "init") {
|
|
7221
7563
|
commandArgs = { generatorPolicyInit: true, inputPath: commandPath(3) };
|
|
7564
|
+
} else if (args[0] === "generator" && args[1] === "policy" && args[2] === "status") {
|
|
7565
|
+
commandArgs = { generatorPolicyStatus: true, inputPath: commandPath(3) };
|
|
7222
7566
|
} else if (args[0] === "generator" && args[1] === "policy" && args[2] === "check") {
|
|
7223
7567
|
commandArgs = { generatorPolicyCheck: true, inputPath: commandPath(3) };
|
|
7224
7568
|
} else if (args[0] === "generator" && args[1] === "policy" && args[2] === "explain") {
|
|
@@ -7422,6 +7766,7 @@ const shouldGeneratorList = Boolean(commandArgs?.generatorList);
|
|
|
7422
7766
|
const shouldGeneratorShow = Boolean(commandArgs?.generatorShow);
|
|
7423
7767
|
const shouldGeneratorCheck = Boolean(commandArgs?.generatorCheck);
|
|
7424
7768
|
const shouldGeneratorPolicyInit = Boolean(commandArgs?.generatorPolicyInit);
|
|
7769
|
+
const shouldGeneratorPolicyStatus = Boolean(commandArgs?.generatorPolicyStatus);
|
|
7425
7770
|
const shouldGeneratorPolicyCheck = Boolean(commandArgs?.generatorPolicyCheck);
|
|
7426
7771
|
const shouldGeneratorPolicyExplain = Boolean(commandArgs?.generatorPolicyExplain);
|
|
7427
7772
|
const shouldGeneratorPolicyPin = Boolean(commandArgs?.generatorPolicyPin);
|
|
@@ -7558,7 +7903,7 @@ const outIndex = args.indexOf("--out");
|
|
|
7558
7903
|
const outPath = outIndex >= 0 ? args[outIndex + 1] : null;
|
|
7559
7904
|
const effectiveOutDir = outDir || outPath || commandArgs?.defaultOutDir || null;
|
|
7560
7905
|
|
|
7561
|
-
if ((shouldCheck || shouldComponentCheck || shouldComponentBehavior || shouldGeneratorCheck || shouldGeneratorPolicyInit || shouldGeneratorPolicyCheck || shouldGeneratorPolicyExplain || shouldGeneratorPolicyPin || shouldValidate || shouldTrustTemplate || shouldTrustStatus || shouldTrustDiff || shouldSourceStatus || shouldTemplateExplain || shouldTemplateStatus || shouldTemplateDetach || shouldTemplatePolicyInit || shouldTemplatePolicyCheck || shouldTemplatePolicyExplain || shouldTemplatePolicyPin || shouldTemplateCheck || shouldTemplateUpdate || generateTarget === "app-bundle") && !inputPath) {
|
|
7906
|
+
if ((shouldCheck || shouldComponentCheck || shouldComponentBehavior || shouldGeneratorCheck || shouldGeneratorPolicyInit || shouldGeneratorPolicyStatus || shouldGeneratorPolicyCheck || shouldGeneratorPolicyExplain || shouldGeneratorPolicyPin || shouldValidate || shouldTrustTemplate || shouldTrustStatus || shouldTrustDiff || shouldSourceStatus || shouldTemplateExplain || shouldTemplateStatus || shouldTemplateDetach || shouldTemplatePolicyInit || shouldTemplatePolicyCheck || shouldTemplatePolicyExplain || shouldTemplatePolicyPin || shouldTemplateCheck || shouldTemplateUpdate || generateTarget === "app-bundle") && !inputPath) {
|
|
7562
7907
|
console.error("Missing required <path>.");
|
|
7563
7908
|
printUsage();
|
|
7564
7909
|
process.exit(1);
|
|
@@ -7612,7 +7957,7 @@ if (shouldQueryShow && !commandArgs?.queryShowName) {
|
|
|
7612
7957
|
process.exit(1);
|
|
7613
7958
|
}
|
|
7614
7959
|
|
|
7615
|
-
if ((shouldCheck || shouldComponentCheck || shouldComponentBehavior || shouldValidate || shouldGeneratorPolicyInit || shouldGeneratorPolicyCheck || shouldGeneratorPolicyExplain || shouldGeneratorPolicyPin || shouldTrustTemplate || shouldTrustStatus || shouldTrustDiff || shouldTemplateExplain || shouldTemplateStatus || shouldTemplatePolicyInit || shouldTemplatePolicyCheck || shouldTemplatePolicyExplain || shouldTemplatePolicyPin || shouldTemplateUpdate || generateTarget === "app-bundle") && inputPath) {
|
|
7960
|
+
if ((shouldCheck || shouldComponentCheck || shouldComponentBehavior || shouldValidate || shouldGeneratorPolicyInit || shouldGeneratorPolicyStatus || shouldGeneratorPolicyCheck || shouldGeneratorPolicyExplain || shouldGeneratorPolicyPin || shouldTrustTemplate || shouldTrustStatus || shouldTrustDiff || shouldTemplateExplain || shouldTemplateStatus || shouldTemplatePolicyInit || shouldTemplatePolicyCheck || shouldTemplatePolicyExplain || shouldTemplatePolicyPin || shouldTemplateUpdate || generateTarget === "app-bundle") && inputPath) {
|
|
7616
7961
|
inputPath = normalizeTopogramPath(inputPath);
|
|
7617
7962
|
}
|
|
7618
7963
|
|
|
@@ -7762,6 +8107,16 @@ try {
|
|
|
7762
8107
|
process.exit(0);
|
|
7763
8108
|
}
|
|
7764
8109
|
|
|
8110
|
+
if (shouldGeneratorPolicyStatus) {
|
|
8111
|
+
const payload = buildGeneratorPolicyStatusPayload(inputPath);
|
|
8112
|
+
if (emitJson) {
|
|
8113
|
+
console.log(stableStringify(payload));
|
|
8114
|
+
} else {
|
|
8115
|
+
printGeneratorPolicyStatusPayload(payload);
|
|
8116
|
+
}
|
|
8117
|
+
process.exit(payload.ok ? 0 : 1);
|
|
8118
|
+
}
|
|
8119
|
+
|
|
7765
8120
|
if (shouldGeneratorPolicyCheck) {
|
|
7766
8121
|
const payload = buildGeneratorPolicyCheckPayload(inputPath);
|
|
7767
8122
|
if (emitJson) {
|
package/src/new-project.js
CHANGED
|
@@ -1975,6 +1975,7 @@ function writeProjectPackage(projectRoot, engineRoot, template) {
|
|
|
1975
1975
|
"template:detach:dry-run": "topogram template detach --dry-run",
|
|
1976
1976
|
"template:policy:check": "topogram template policy check",
|
|
1977
1977
|
"template:policy:explain": "topogram template policy explain",
|
|
1978
|
+
"generator:policy:status": "topogram generator policy status",
|
|
1978
1979
|
"generator:policy:check": "topogram generator policy check",
|
|
1979
1980
|
"generator:policy:explain": "topogram generator policy explain",
|
|
1980
1981
|
"template:update:status": "topogram template update --status",
|
|
@@ -2052,6 +2053,7 @@ Useful inspection:
|
|
|
2052
2053
|
npm run template:detach:dry-run
|
|
2053
2054
|
npm run template:policy:check
|
|
2054
2055
|
npm run template:policy:explain
|
|
2056
|
+
npm run generator:policy:status
|
|
2055
2057
|
npm run generator:policy:check
|
|
2056
2058
|
npm run generator:policy:explain
|
|
2057
2059
|
npm run template:update:status
|
|
@@ -2084,6 +2086,7 @@ function writeProjectReadme(projectRoot, projectConfig) {
|
|
|
2084
2086
|
"npm run template:explain",
|
|
2085
2087
|
"npm run check",
|
|
2086
2088
|
"npm run template:policy:check",
|
|
2089
|
+
"npm run generator:policy:status",
|
|
2087
2090
|
"npm run generator:policy:check",
|
|
2088
2091
|
...(template.includesExecutableImplementation ? [
|
|
2089
2092
|
"npm run template:policy:explain",
|