@pnpm/read-project-manifest
Version:
Read a project manifest (called package.json in most cases)
221 lines • 8.11 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.safeReadProjectManifestOnly = safeReadProjectManifestOnly;
exports.readProjectManifest = readProjectManifest;
exports.readProjectManifestOnly = readProjectManifestOnly;
exports.tryReadProjectManifest = tryReadProjectManifest;
exports.readExactProjectManifest = readExactProjectManifest;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const error_1 = require("@pnpm/error");
const text_comments_parser_1 = require("@pnpm/text.comments-parser");
const write_project_manifest_1 = require("@pnpm/write-project-manifest");
const read_yaml_file_1 = __importDefault(require("read-yaml-file"));
const detect_indent_1 = __importDefault(require("@gwhitney/detect-indent"));
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
const is_windows_1 = __importDefault(require("is-windows"));
const readFile_1 = require("./readFile");
async function safeReadProjectManifestOnly(projectDir) {
try {
return await readProjectManifestOnly(projectDir);
}
catch (err) { // eslint-disable-line
if (err.code === 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND') {
return null;
}
throw err;
}
}
async function readProjectManifest(projectDir) {
const result = await tryReadProjectManifest(projectDir);
if (result.manifest !== null) {
return result;
}
throw new error_1.PnpmError('NO_IMPORTER_MANIFEST_FOUND', `No package.json (or package.yaml, or package.json5) was found in "${projectDir}".`);
}
async function readProjectManifestOnly(projectDir) {
const { manifest } = await readProjectManifest(projectDir);
return manifest;
}
async function tryReadProjectManifest(projectDir) {
try {
const manifestPath = path_1.default.join(projectDir, 'package.json');
const { data, text } = await (0, readFile_1.readJsonFile)(manifestPath);
return {
fileName: 'package.json',
manifest: data,
writeProjectManifest: createManifestWriter({
...detectFileFormatting(text),
initialManifest: data,
manifestPath,
}),
};
}
catch (err) { // eslint-disable-line
if (err.code !== 'ENOENT')
throw err;
}
try {
const manifestPath = path_1.default.join(projectDir, 'package.json5');
const { data, text } = await (0, readFile_1.readJson5File)(manifestPath);
return {
fileName: 'package.json5',
manifest: data,
writeProjectManifest: createManifestWriter({
...detectFileFormattingAndComments(text),
initialManifest: data,
manifestPath,
}),
};
}
catch (err) { // eslint-disable-line
if (err.code !== 'ENOENT')
throw err;
}
try {
const manifestPath = path_1.default.join(projectDir, 'package.yaml');
const manifest = await readPackageYaml(manifestPath);
return {
fileName: 'package.yaml',
manifest,
writeProjectManifest: createManifestWriter({ initialManifest: manifest, manifestPath }),
};
}
catch (err) { // eslint-disable-line
if (err.code !== 'ENOENT')
throw err;
}
if ((0, is_windows_1.default)()) {
// ENOTDIR isn't used on Windows, but pnpm expects it.
let s;
try {
s = await fs_1.promises.stat(projectDir);
}
catch (err) { // eslint-disable-line
// Ignore
}
if ((s != null) && !s.isDirectory()) {
const err = new Error(`"${projectDir}" is not a directory`);
// @ts-expect-error
err['code'] = 'ENOTDIR';
throw err;
}
}
const filePath = path_1.default.join(projectDir, 'package.json');
return {
fileName: 'package.json',
manifest: null,
writeProjectManifest: async (manifest) => (0, write_project_manifest_1.writeProjectManifest)(filePath, manifest),
};
}
function detectFileFormattingAndComments(text) {
const { comments, text: newText, hasFinalNewline } = (0, text_comments_parser_1.extractComments)(text);
return {
comments,
indent: (0, detect_indent_1.default)(newText).indent,
insertFinalNewline: hasFinalNewline,
};
}
function detectFileFormatting(text) {
return {
indent: (0, detect_indent_1.default)(text).indent,
insertFinalNewline: text.endsWith('\n'),
};
}
async function readExactProjectManifest(manifestPath) {
const base = path_1.default.basename(manifestPath).toLowerCase();
switch (base) {
case 'package.json': {
const { data, text } = await (0, readFile_1.readJsonFile)(manifestPath);
return {
manifest: data,
writeProjectManifest: createManifestWriter({
...detectFileFormatting(text),
initialManifest: data,
manifestPath,
}),
};
}
case 'package.json5': {
const { data, text } = await (0, readFile_1.readJson5File)(manifestPath);
return {
manifest: data,
writeProjectManifest: createManifestWriter({
...detectFileFormattingAndComments(text),
initialManifest: data,
manifestPath,
}),
};
}
case 'package.yaml': {
const manifest = await readPackageYaml(manifestPath);
return {
manifest,
writeProjectManifest: createManifestWriter({ initialManifest: manifest, manifestPath }),
};
}
}
throw new Error(`Not supported manifest name "${base}"`);
}
async function readPackageYaml(filePath) {
try {
return await (0, read_yaml_file_1.default)(filePath);
}
catch (err) { // eslint-disable-line
if (err.name !== 'YAMLException')
throw err;
err.message = `${err.message}\nin ${filePath}`;
err.code = 'ERR_PNPM_YAML_PARSE';
throw err;
}
}
function createManifestWriter(opts) {
let initialManifest = normalize(opts.initialManifest);
return async (updatedManifest, force) => {
updatedManifest = normalize(updatedManifest);
if (force === true || !(0, fast_deep_equal_1.default)(initialManifest, updatedManifest)) {
await (0, write_project_manifest_1.writeProjectManifest)(opts.manifestPath, updatedManifest, {
comments: opts.comments,
indent: opts.indent,
insertFinalNewline: opts.insertFinalNewline,
});
initialManifest = normalize(updatedManifest);
return Promise.resolve(undefined);
}
return Promise.resolve(undefined);
};
}
const dependencyKeys = new Set([
'dependencies',
'devDependencies',
'optionalDependencies',
'peerDependencies',
]);
function normalize(manifest) {
const result = {};
for (const key in manifest) {
if (Object.prototype.hasOwnProperty.call(manifest, key)) {
const value = manifest[key];
if (typeof value !== 'object' || !dependencyKeys.has(key)) {
result[key] = structuredClone(value);
}
else {
const keys = Object.keys(value);
if (keys.length !== 0) {
keys.sort();
const sortedValue = {};
for (const k of keys) {
// @ts-expect-error this is fine
sortedValue[k] = value[k];
}
result[key] = sortedValue;
}
}
}
}
return result;
}
//# sourceMappingURL=index.js.map