prettier-plugin-pkg
Version:
An opinionated package.json formatter plugin for Prettier
483 lines (475 loc) • 10.6 kB
JavaScript
;
var babelParser = require('prettier/plugins/babel.js');
const DEFAULT_LIFECYCLE_SCRIPTS = /* @__PURE__ */ new Set([
"dependencies",
"install",
"pack",
"prepare",
"publish",
"restart",
"shrinkwrap",
"start",
"stop",
"test",
"version"
]);
function alphabetSort(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
const sortObject = (a, b) => alphabetSort(a.key.value, b.key.value);
const sortStringArray = (a, b) => alphabetSort(a.value, b.value);
const getScriptSortProps = (scriptName, allScriptNames) => {
if (DEFAULT_LIFECYCLE_SCRIPTS.has(scriptName)) {
return {
base: scriptName,
order: 0
};
}
if (scriptName.length > 3 && scriptName.startsWith("pre")) {
const base = scriptName.slice(3);
if (allScriptNames.has(base) || DEFAULT_LIFECYCLE_SCRIPTS.has(base)) {
return {
base,
order: -1
};
}
}
if (scriptName.length > 4 && scriptName.startsWith("post")) {
const base = scriptName.slice(4);
if (allScriptNames.has(base) || DEFAULT_LIFECYCLE_SCRIPTS.has(base)) {
return {
base,
order: 1
};
}
}
return {
base: scriptName,
order: 0
};
};
const sortScriptNames = (scriptNames) => {
const scriptNameSet = new Set(scriptNames);
scriptNames = [...scriptNameSet];
return scriptNames.sort((a, b) => {
const left = getScriptSortProps(a, scriptNameSet);
const right = getScriptSortProps(b, scriptNameSet);
if (left.base !== right.base) {
return alphabetSort(left.base, right.base);
}
if (left.order !== right.order) {
return alphabetSort(left.order, right.order);
}
return alphabetSort(a, b);
});
};
const sortScripts = (props) => {
const scriptOrder = Object.fromEntries(
sortScriptNames(props.map((prop) => prop.key.value)).map((name, index) => [
name,
index
])
);
props.sort(
(a, b) => alphabetSort(scriptOrder[a.key.value], scriptOrder[b.key.value])
);
};
const process$1 = (props) => {
const filesNode = props.find((prop) => prop.key.value === "files");
if (!filesNode) {
return props;
}
let readme;
let license;
const filesNodeValue = filesNode.value;
const [normals, negations] = filesNodeValue.elements.reduce(
(acc, node) => {
const value = node.value.toLowerCase();
if (value === "license") {
license = node;
return acc;
}
if (value === "readme" || value === "readme.md") {
readme = node;
return acc;
}
acc[+value.startsWith("!")].push(node);
return acc;
},
[[], []]
);
normals.sort(sortStringArray);
negations.sort(sortStringArray);
if (readme) {
normals.push(readme);
}
if (license) {
normals.push(license);
}
filesNodeValue.elements = [...normals, ...negations];
return props;
};
const process = (props, key) => {
const item = props.find((prop) => prop.key.value === key);
if (item) {
if ("elements" in item.value) {
item.value.elements.sort(sortStringArray);
} else if ("properties" in item.value) {
if (key === "scripts") {
sortScripts(item.value.properties);
} else {
item.value.properties.sort(sortObject);
}
}
}
return props;
};
const dependencyNames = [
"bundledDependencies",
"peerDependencies",
"peerDependenciesMeta",
"dependencies",
"dependenciesMeta",
"optionalDependencies",
"devDependencies",
"overrides",
"resolutions"
];
const NPM_SORTS = [
"$schema",
"name",
"version",
"description",
"keywords",
"homepage",
"bugs",
"license",
"author",
"contributors",
"funding",
"files",
"exports",
"main",
"browser",
"bin",
"man",
"directories",
"repository",
"scripts",
"config",
"dependencies",
"devDependencies",
"peerDependencies",
"peerDependenciesMeta",
"bundleDependencies",
"optionalDependencies",
"overrides",
"engines",
"os",
"cpu",
"libc",
"devEngines",
"private",
"publishConfig",
"workspaces"
];
const NPM_PLUS_SORTS = [
"$schema",
"name",
"displayName",
"version",
"private",
"description",
"categories",
"keywords",
"homepage",
"bugs",
"repository",
"funding",
"license",
"qna",
"author",
"maintainers",
"contributors",
"publisher",
"sideEffects",
"type",
"imports",
"exports",
"main",
"svelte",
"umd:main",
"jsdelivr",
"unpkg",
"module",
"source",
"jsnext:main",
"browser",
"react-native",
"types",
"typesVersions",
"typings",
"style",
"example",
"examplestyle",
"assets",
"bin",
"man",
"directories",
"files",
"workspaces",
"binary",
"scripts",
"betterScripts",
"contributes",
"activationEvents",
"husky",
"simple-git-hooks",
"pre-commit",
"commitlint",
"lint-staged",
"nano-staged",
"config",
"nodemonConfig",
"browserify",
"babel",
"browserslist",
"xo",
"prettier",
"eslintConfig",
"eslintIgnore",
"npmpackagejsonlint",
"release",
"remarkConfig",
"stylelint",
"ava",
"jest",
"mocha",
"nyc",
"tap",
"oclif",
"resolutions",
"dependencies",
"devDependencies",
"dependenciesMeta",
"peerDependencies",
"peerDependenciesMeta",
"optionalDependencies",
"bundledDependencies",
"bundleDependencies",
"extensionPack",
"extensionDependencies",
"flat",
"packageManager",
"engines",
"engineStrict",
"volta",
"languageName",
"os",
"cpu",
"preferGlobal",
"publishConfig",
"icon",
"badges",
"galleryBanner",
"preview",
"markdown",
"pnpm"
];
const primary = [
// schema definition
"$schema",
// meta
"name",
"version",
"type",
"flat",
"displayName",
"description",
"categories",
"repository",
"homepage",
"bugs",
"author",
"publisher",
"maintainers",
"contributors",
"donate",
"funding",
"sponsor",
"license",
"preview",
"private",
"workspaces",
// constraints
"languageName",
"packageManager",
"engines",
"cpu",
"os",
"libc",
"devEngines",
// entries
"man",
"bin",
"main",
"types",
"typings",
"typesVersions",
"module",
"imports",
"exports",
"esnext",
"es2020",
"esm2020",
"fesm2020",
"es2015",
"esm2015",
"fesm2015",
"es5",
"esm5",
"fesm5",
"browser",
"umd",
"jsdelivr",
"unpkg",
// contents and utils
"directories",
"files",
"keywords",
"scripts",
"config",
// dependencies
...dependencyNames,
"publishConfig",
"sideEffects",
// vscode spec
"icon",
"badges",
"galleryBanner",
"activationEvents",
"contributes",
"markdown",
"qna",
"extensionPack",
"extensionDependencies",
"extensionKind"
];
const DEFAULT_SORT_ORDERS = {
npm: NPM_SORTS,
"npm-plus": NPM_PLUS_SORTS
};
const uniqueArray = (arr) => {
return [...new Set(arr)];
};
const sort = (props, options) => {
let { packageSortOrder, packageSortOrderPreset } = options;
const defaultSortOrder = (
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
packageSortOrderPreset && DEFAULT_SORT_ORDERS[packageSortOrderPreset] || []
);
packageSortOrder = uniqueArray([
...packageSortOrder != null ? packageSortOrder : [],
...defaultSortOrder,
...primary
]);
const others = [];
const known = props.filter((prop) => {
if (packageSortOrder.includes(prop.key.value)) {
return true;
}
others.push(prop);
return false;
});
known.sort(
(a, b) => alphabetSort(
packageSortOrder.indexOf(a.key.value),
packageSortOrder.indexOf(b.key.value)
)
);
others.sort(sortObject);
return [...known, ...others];
};
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
const PKG_REG = /[/\\]?package\.json$/;
const {
json: { parse }
} = babelParser.parsers;
const DEFAULT_SORTS = ["engines", "devEngines", "scripts", ...dependencyNames];
const format = (properties, options) => {
const { packageIgnoreSort } = options;
let props = ((packageIgnoreSort == null ? void 0 : packageIgnoreSort.length) ? DEFAULT_SORTS.filter((item) => !packageIgnoreSort.includes(item)) : DEFAULT_SORTS).reduce((acc, item) => process(acc, item), sort(properties, options));
props = process$1(props);
return props;
};
var index = {
name: "prettier-plugin-pkg",
parsers: {
"json-stringify": __spreadProps(__spreadValues({}, babelParser.parsers["json-stringify"]), {
parse(text, options) {
const { filepath } = options;
const ast = parse(text, options);
if (PKG_REG.test(filepath)) {
ast.node.properties = format(
ast.node.properties,
options
);
}
return ast;
}
})
},
options: {
packageSortOrder: {
since: "0.21.0",
category: "Package",
type: "string",
array: true,
default: [{ value: [] }],
description: "An array of property names to sort the package.json properties by."
},
packageIgnoreSort: {
since: "0.21.0",
category: "Package",
type: "string",
array: true,
default: [{ value: [] }],
description: "An array of property names to ignore when sorting the package.json properties."
},
packageSortOrderPreset: {
since: "0.21.1",
category: "Package",
type: "choice",
choices: [
{
value: "npm",
description: "Sorted according to https://docs.npmjs.com/cli/v11/configuring-npm/package-json."
},
{
value: "npm-plus",
description: "Sorted according to https://github.com/keithamus/sort-package-json/blob/aa6774ad937feb83178c8bc981f08305e1d22b5c/defaultRules.md."
}
],
description: "A preset for the package.json sort order."
}
}
};
module.exports = index;