@jpp-toolkit/sort-package-json 0.0.11
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/LICENSE.md +21 -0
- package/index.d.mts +33 -0
- package/index.mjs +421 -0
- package/package.json +51 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Julien Papini
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/index.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
type ComparatorFunction = (left: string, right: string) => number;
|
|
4
|
+
|
|
5
|
+
interface Options {
|
|
6
|
+
readonly sortOrder?: readonly string[] | ComparatorFunction;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SortPackageJson {
|
|
10
|
+
/**
|
|
11
|
+
* Sort packageJson object.
|
|
12
|
+
*
|
|
13
|
+
* @param packageJson - A packageJson.
|
|
14
|
+
* @param options - An options object.
|
|
15
|
+
* @returns Sorted packageJson object.
|
|
16
|
+
*/
|
|
17
|
+
<T extends Record<any, any>>(packageJson: T, options?: Options): T;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sort packageJson string.
|
|
21
|
+
*
|
|
22
|
+
* @param packageJson - A packageJson string.
|
|
23
|
+
* @param options - An options object.
|
|
24
|
+
* @returns Sorted packageJson string.
|
|
25
|
+
*/
|
|
26
|
+
(packageJson: string, options?: Options): string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare const sortPackageJsonDefault: SortPackageJson;
|
|
30
|
+
export default sortPackageJsonDefault;
|
|
31
|
+
|
|
32
|
+
export const sortPackageJson: SortPackageJson;
|
|
33
|
+
export const sortOrder: string[];
|
package/index.mjs
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import sortObjectKeys from 'sort-object-keys';
|
|
3
|
+
import detectIndent from 'detect-indent';
|
|
4
|
+
import { detectNewlineGraceful as detectNewline } from 'detect-newline';
|
|
5
|
+
import gitHooks from 'git-hooks-list';
|
|
6
|
+
import isPlainObject from 'is-plain-obj';
|
|
7
|
+
import semver from 'semver';
|
|
8
|
+
|
|
9
|
+
const pipe =
|
|
10
|
+
(fns) =>
|
|
11
|
+
(x, ...args) =>
|
|
12
|
+
fns.reduce((result, fn) => fn(result, ...args), x);
|
|
13
|
+
const onArray = (fn) => (x) => (Array.isArray(x) ? fn(x) : x);
|
|
14
|
+
const onStringArray = (fn) => (x) =>
|
|
15
|
+
Array.isArray(x) && x.every((item) => typeof item === 'string') ? fn(x) : x;
|
|
16
|
+
const uniq = onStringArray((xs) => [...new Set(xs)]);
|
|
17
|
+
const sortArray = onStringArray((array) => array.toSorted());
|
|
18
|
+
const uniqAndSortArray = pipe([uniq, sortArray]);
|
|
19
|
+
const onObject =
|
|
20
|
+
(fn) =>
|
|
21
|
+
(x, ...args) =>
|
|
22
|
+
isPlainObject(x) ? fn(x, ...args) : x;
|
|
23
|
+
const sortObjectBy = (comparator, deep) => {
|
|
24
|
+
const over = onObject((object) => {
|
|
25
|
+
if (deep) {
|
|
26
|
+
object = Object.fromEntries(
|
|
27
|
+
Object.entries(object).map(([key, value]) => [key, over(value)]),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return sortObjectKeys(object, comparator);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return over;
|
|
35
|
+
};
|
|
36
|
+
const sortObject = sortObjectBy();
|
|
37
|
+
const sortURLObject = sortObjectBy(['type', 'url']);
|
|
38
|
+
const sortPeopleObject = sortObjectBy(['name', 'email', 'url']);
|
|
39
|
+
const sortDirectories = sortObjectBy(['lib', 'bin', 'man', 'doc', 'example', 'test']);
|
|
40
|
+
const overProperty =
|
|
41
|
+
(property, over) =>
|
|
42
|
+
(object, ...args) =>
|
|
43
|
+
Object.hasOwn(object, property) ?
|
|
44
|
+
{ ...object, [property]: over(object[property], ...args) }
|
|
45
|
+
: object;
|
|
46
|
+
const sortGitHooks = sortObjectBy(gitHooks);
|
|
47
|
+
|
|
48
|
+
const parseNameAndVersionRange = (specifier) => {
|
|
49
|
+
// Ignore anything after > & rely on fallback alphanumeric sorting for that
|
|
50
|
+
const [nameAndVersion] = specifier.split('>');
|
|
51
|
+
const atMatches = [...nameAndVersion.matchAll('@')];
|
|
52
|
+
if (!atMatches.length || (atMatches.length === 1 && atMatches[0].index === 0)) {
|
|
53
|
+
return { name: specifier };
|
|
54
|
+
}
|
|
55
|
+
const splitIndex = atMatches.pop().index;
|
|
56
|
+
return {
|
|
57
|
+
name: nameAndVersion.substring(0, splitIndex),
|
|
58
|
+
range: nameAndVersion.substring(splitIndex + 1),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const sortObjectBySemver = sortObjectBy((a, b) => {
|
|
63
|
+
const { name: aName, range: aRange } = parseNameAndVersionRange(a);
|
|
64
|
+
const { name: bName, range: bRange } = parseNameAndVersionRange(b);
|
|
65
|
+
|
|
66
|
+
if (aName !== bName) {
|
|
67
|
+
return aName.localeCompare(bName, 'en');
|
|
68
|
+
}
|
|
69
|
+
if (!aRange) {
|
|
70
|
+
return -1;
|
|
71
|
+
}
|
|
72
|
+
if (!bRange) {
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
return semver.compare(semver.minVersion(aRange), semver.minVersion(bRange));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const getPackageName = (ident) => {
|
|
79
|
+
const index = ident.indexOf('@', ident.startsWith('@') ? 1 : 0);
|
|
80
|
+
// This should not happen, unless user manually edit the package.json file
|
|
81
|
+
return index === -1 ? ident : ident.slice(0, index);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const sortObjectByIdent = (a, b) => {
|
|
85
|
+
const packageNameA = getPackageName(a);
|
|
86
|
+
const packageNameB = getPackageName(b);
|
|
87
|
+
|
|
88
|
+
if (packageNameA < packageNameB) return -1;
|
|
89
|
+
if (packageNameA > packageNameB) return 1;
|
|
90
|
+
return 0;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// sort deps like the npm CLI does (via the package @npmcli/package-json)
|
|
94
|
+
// https://github.com/npm/package-json/blob/b6465f44c727d6513db6898c7cbe41dd355cebe8/lib/update-dependencies.js#L8-L21
|
|
95
|
+
const sortDependenciesLikeNpm = sortObjectBy((a, b) => a.localeCompare(b, 'en'));
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* "workspaces" can be an array (npm or yarn classic) or an object (pnpm/bun).
|
|
99
|
+
* In the case of an array, we do not want to alphabetically sort it in case
|
|
100
|
+
* scripts need to run in a specific order.
|
|
101
|
+
*
|
|
102
|
+
* @see https://docs.npmjs.com/cli/v7/using-npm/workspaces?v=true#running-commands-in-the-context-of-workspaces
|
|
103
|
+
*/
|
|
104
|
+
const sortWorkspaces = (workspaces) => {
|
|
105
|
+
if (!isPlainObject(workspaces)) {
|
|
106
|
+
return workspaces;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Sort known properties in a specific order
|
|
110
|
+
const sortedWorkspaces = {};
|
|
111
|
+
|
|
112
|
+
// First add packages if it exists
|
|
113
|
+
if (workspaces.packages) {
|
|
114
|
+
sortedWorkspaces.packages = uniqAndSortArray(workspaces.packages);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Then add catalog if it exists and sort it like dependencies
|
|
118
|
+
if (workspaces.catalog) {
|
|
119
|
+
sortedWorkspaces.catalog = sortDependenciesLikeNpm(workspaces.catalog);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Add any other properties in alphabetical order
|
|
123
|
+
const knownKeys = ['packages', 'catalog'];
|
|
124
|
+
const otherKeys = Object.keys(workspaces)
|
|
125
|
+
.filter((key) => !knownKeys.includes(key))
|
|
126
|
+
.sort();
|
|
127
|
+
|
|
128
|
+
for (const key of otherKeys) {
|
|
129
|
+
sortedWorkspaces[key] = workspaces[key];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return sortedWorkspaces;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// https://github.com/eslint/eslint/blob/acc0e47572a9390292b4e313b4a4bf360d236358/conf/config-schema.js
|
|
136
|
+
const eslintBaseConfigProperties = [
|
|
137
|
+
// `files` and `excludedFiles` are only on `overrides[]`
|
|
138
|
+
// for easier sort `overrides[]`,
|
|
139
|
+
// add them to here, so we don't need sort `overrides[]` twice
|
|
140
|
+
'files',
|
|
141
|
+
'excludedFiles',
|
|
142
|
+
// baseConfig
|
|
143
|
+
'env',
|
|
144
|
+
'parser',
|
|
145
|
+
'parserOptions',
|
|
146
|
+
'settings',
|
|
147
|
+
'plugins',
|
|
148
|
+
'extends',
|
|
149
|
+
'rules',
|
|
150
|
+
'overrides',
|
|
151
|
+
'globals',
|
|
152
|
+
'processor',
|
|
153
|
+
'noInlineConfig',
|
|
154
|
+
'reportUnusedDisableDirectives',
|
|
155
|
+
];
|
|
156
|
+
const sortEslintConfig = onObject(
|
|
157
|
+
pipe([
|
|
158
|
+
sortObjectBy(eslintBaseConfigProperties),
|
|
159
|
+
overProperty('env', sortObject),
|
|
160
|
+
overProperty('globals', sortObject),
|
|
161
|
+
overProperty(
|
|
162
|
+
'overrides',
|
|
163
|
+
onArray((overrides) => overrides.map(sortEslintConfig)),
|
|
164
|
+
),
|
|
165
|
+
overProperty('parserOptions', sortObject),
|
|
166
|
+
overProperty(
|
|
167
|
+
'rules',
|
|
168
|
+
sortObjectBy(
|
|
169
|
+
(rule1, rule2) =>
|
|
170
|
+
rule1.split('/').length - rule2.split('/').length || rule1.localeCompare(rule2),
|
|
171
|
+
),
|
|
172
|
+
),
|
|
173
|
+
overProperty('settings', sortObject),
|
|
174
|
+
]),
|
|
175
|
+
);
|
|
176
|
+
const sortVSCodeBadgeObject = sortObjectBy(['description', 'url', 'href']);
|
|
177
|
+
|
|
178
|
+
const sortPrettierConfig = onObject(
|
|
179
|
+
pipe([
|
|
180
|
+
// sort keys alphabetically, but put `overrides` at bottom
|
|
181
|
+
(config) =>
|
|
182
|
+
sortObjectKeys(config, [
|
|
183
|
+
...Object.keys(config)
|
|
184
|
+
.filter((key) => key !== 'overrides')
|
|
185
|
+
.sort(),
|
|
186
|
+
'overrides',
|
|
187
|
+
]),
|
|
188
|
+
// if `config.overrides` exists
|
|
189
|
+
overProperty(
|
|
190
|
+
'overrides',
|
|
191
|
+
// and `config.overrides` is an array
|
|
192
|
+
onArray((overrides) =>
|
|
193
|
+
overrides.map(
|
|
194
|
+
pipe([
|
|
195
|
+
// sort `config.overrides[]` alphabetically
|
|
196
|
+
sortObject,
|
|
197
|
+
// sort `config.overrides[].options` alphabetically
|
|
198
|
+
overProperty('options', sortObject),
|
|
199
|
+
]),
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
]),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const sortVolta = sortObjectBy(['node', 'npm', 'yarn']);
|
|
207
|
+
|
|
208
|
+
const pnpmBaseConfigProperties = [
|
|
209
|
+
'peerDependencyRules',
|
|
210
|
+
'neverBuiltDependencies',
|
|
211
|
+
'onlyBuiltDependencies',
|
|
212
|
+
'onlyBuiltDependenciesFile',
|
|
213
|
+
'allowedDeprecatedVersions',
|
|
214
|
+
'allowNonAppliedPatches',
|
|
215
|
+
'updateConfig',
|
|
216
|
+
'auditConfig',
|
|
217
|
+
'requiredScripts',
|
|
218
|
+
'supportedArchitectures',
|
|
219
|
+
'overrides',
|
|
220
|
+
'patchedDependencies',
|
|
221
|
+
'packageExtensions',
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const sortPnpmConfig = onObject(
|
|
225
|
+
pipe([
|
|
226
|
+
sortObjectBy(pnpmBaseConfigProperties, true),
|
|
227
|
+
overProperty('overrides', sortObjectBySemver),
|
|
228
|
+
]),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// fields marked `vscode` are for `Visual Studio Code extension manifest` only
|
|
232
|
+
// https://code.visualstudio.com/api/references/extension-manifest
|
|
233
|
+
// Supported fields:
|
|
234
|
+
// publisher, displayName, categories, galleryBanner, preview, contributes,
|
|
235
|
+
// activationEvents, badges, markdown, qna, extensionPack,
|
|
236
|
+
// extensionDependencies, icon
|
|
237
|
+
|
|
238
|
+
// field.key{string}: field name
|
|
239
|
+
// field.over{function}: sort field subKey
|
|
240
|
+
const fields = [
|
|
241
|
+
{ key: '$schema' },
|
|
242
|
+
{ key: 'name' },
|
|
243
|
+
/* vscode */ { key: 'displayName' },
|
|
244
|
+
{ key: 'version' },
|
|
245
|
+
/* yarn */ { key: 'stableVersion' },
|
|
246
|
+
{ key: 'private' },
|
|
247
|
+
{ key: 'description' },
|
|
248
|
+
/* vscode */ { key: 'categories', over: uniq },
|
|
249
|
+
{ key: 'keywords', over: uniq },
|
|
250
|
+
{ key: 'homepage' },
|
|
251
|
+
{ key: 'bugs', over: sortObjectBy(['url', 'email']) },
|
|
252
|
+
{ key: 'repository', over: sortURLObject },
|
|
253
|
+
{ key: 'funding', over: sortURLObject },
|
|
254
|
+
{ key: 'license', over: sortURLObject },
|
|
255
|
+
/* vscode */ { key: 'qna' },
|
|
256
|
+
{ key: 'author', over: sortPeopleObject },
|
|
257
|
+
{
|
|
258
|
+
key: 'maintainers',
|
|
259
|
+
over: onArray((maintainers) => maintainers.map(sortPeopleObject)),
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
key: 'contributors',
|
|
263
|
+
over: onArray((contributors) => contributors.map(sortPeopleObject)),
|
|
264
|
+
},
|
|
265
|
+
/* vscode */ { key: 'publisher' },
|
|
266
|
+
{ key: 'sideEffects' },
|
|
267
|
+
{ key: 'type' },
|
|
268
|
+
{ key: 'imports' },
|
|
269
|
+
{ key: 'exports' },
|
|
270
|
+
{ key: 'main' },
|
|
271
|
+
{ key: 'svelte' },
|
|
272
|
+
{ key: 'umd:main' },
|
|
273
|
+
{ key: 'jsdelivr' },
|
|
274
|
+
{ key: 'unpkg' },
|
|
275
|
+
{ key: 'module' },
|
|
276
|
+
{ key: 'source' },
|
|
277
|
+
{ key: 'jsnext:main' },
|
|
278
|
+
{ key: 'browser' },
|
|
279
|
+
{ key: 'react-native' },
|
|
280
|
+
{ key: 'types' },
|
|
281
|
+
{ key: 'typesVersions' },
|
|
282
|
+
{ key: 'typings' },
|
|
283
|
+
{ key: 'style' },
|
|
284
|
+
{ key: 'example' },
|
|
285
|
+
{ key: 'examplestyle' },
|
|
286
|
+
{ key: 'assets' },
|
|
287
|
+
{ key: 'bin', over: sortObject },
|
|
288
|
+
{ key: 'man' },
|
|
289
|
+
{ key: 'directories', over: sortDirectories },
|
|
290
|
+
{ key: 'files', over: uniq },
|
|
291
|
+
{ key: 'workspaces', over: sortWorkspaces },
|
|
292
|
+
// node-pre-gyp https://www.npmjs.com/package/node-pre-gyp#1-add-new-entries-to-your-packagejson
|
|
293
|
+
{
|
|
294
|
+
key: 'binary',
|
|
295
|
+
over: sortObjectBy(['module_name', 'module_path', 'remote_path', 'package_name', 'host']),
|
|
296
|
+
},
|
|
297
|
+
{ key: 'scripts' },
|
|
298
|
+
{ key: 'betterScripts' },
|
|
299
|
+
/* vscode */ { key: 'l10n' },
|
|
300
|
+
/* vscode */ { key: 'contributes', over: sortObject },
|
|
301
|
+
/* vscode */ { key: 'activationEvents', over: uniq },
|
|
302
|
+
{ key: 'husky', over: overProperty('hooks', sortGitHooks) },
|
|
303
|
+
{ key: 'simple-git-hooks', over: sortGitHooks },
|
|
304
|
+
{ key: 'pre-commit' },
|
|
305
|
+
{ key: 'commitlint', over: sortObject },
|
|
306
|
+
{ key: 'lint-staged' },
|
|
307
|
+
{ key: 'nano-staged' },
|
|
308
|
+
{ key: 'config', over: sortObject },
|
|
309
|
+
{ key: 'nodemonConfig', over: sortObject },
|
|
310
|
+
{ key: 'browserify', over: sortObject },
|
|
311
|
+
{ key: 'babel', over: sortObject },
|
|
312
|
+
{ key: 'browserslist' },
|
|
313
|
+
{ key: 'xo', over: sortObject },
|
|
314
|
+
{ key: 'prettier', over: sortPrettierConfig },
|
|
315
|
+
{ key: 'eslintConfig', over: sortEslintConfig },
|
|
316
|
+
{ key: 'eslintIgnore' },
|
|
317
|
+
{ key: 'npmpkgjsonlint', over: sortObject },
|
|
318
|
+
{ key: 'npmPackageJsonLintConfig', over: sortObject },
|
|
319
|
+
{ key: 'npmpackagejsonlint', over: sortObject },
|
|
320
|
+
{ key: 'release', over: sortObject },
|
|
321
|
+
{ key: 'remarkConfig', over: sortObject },
|
|
322
|
+
{ key: 'stylelint' },
|
|
323
|
+
{ key: 'ava', over: sortObject },
|
|
324
|
+
{ key: 'jest', over: sortObject },
|
|
325
|
+
{ key: 'jest-junit', over: sortObject },
|
|
326
|
+
{ key: 'jest-stare', over: sortObject },
|
|
327
|
+
{ key: 'mocha', over: sortObject },
|
|
328
|
+
{ key: 'nyc', over: sortObject },
|
|
329
|
+
{ key: 'c8', over: sortObject },
|
|
330
|
+
{ key: 'tap', over: sortObject },
|
|
331
|
+
{ key: 'oclif', over: sortObjectBy(undefined, true) },
|
|
332
|
+
{ key: 'resolutions', over: sortObject },
|
|
333
|
+
{ key: 'overrides', over: sortDependenciesLikeNpm },
|
|
334
|
+
{ key: 'dependencies', over: sortDependenciesLikeNpm },
|
|
335
|
+
{ key: 'devDependencies', over: sortDependenciesLikeNpm },
|
|
336
|
+
{ key: 'dependenciesMeta', over: sortObjectBy(sortObjectByIdent, true) },
|
|
337
|
+
{ key: 'peerDependencies', over: sortDependenciesLikeNpm },
|
|
338
|
+
// TODO: only sort depth = 2
|
|
339
|
+
{ key: 'peerDependenciesMeta', over: sortObjectBy(undefined, true) },
|
|
340
|
+
{ key: 'optionalDependencies', over: sortDependenciesLikeNpm },
|
|
341
|
+
{ key: 'bundledDependencies', over: uniqAndSortArray },
|
|
342
|
+
{ key: 'bundleDependencies', over: uniqAndSortArray },
|
|
343
|
+
/* vscode */ { key: 'extensionPack', over: uniqAndSortArray },
|
|
344
|
+
/* vscode */ { key: 'extensionDependencies', over: uniqAndSortArray },
|
|
345
|
+
{ key: 'flat' },
|
|
346
|
+
{ key: 'packageManager' },
|
|
347
|
+
{ key: 'engines', over: sortObject },
|
|
348
|
+
{ key: 'engineStrict', over: sortObject },
|
|
349
|
+
{ key: 'volta', over: sortVolta },
|
|
350
|
+
{ key: 'languageName' },
|
|
351
|
+
{ key: 'os' },
|
|
352
|
+
{ key: 'cpu' },
|
|
353
|
+
{ key: 'preferGlobal', over: sortObject },
|
|
354
|
+
{ key: 'publishConfig', over: sortObject },
|
|
355
|
+
/* vscode */ { key: 'icon' },
|
|
356
|
+
/* vscode */ {
|
|
357
|
+
key: 'badges',
|
|
358
|
+
over: onArray((badge) => badge.map(sortVSCodeBadgeObject)),
|
|
359
|
+
},
|
|
360
|
+
/* vscode */ { key: 'galleryBanner', over: sortObject },
|
|
361
|
+
/* vscode */ { key: 'preview' },
|
|
362
|
+
/* vscode */ { key: 'markdown' },
|
|
363
|
+
{ key: 'pnpm', over: sortPnpmConfig },
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
const defaultSortOrder = fields.map(({ key }) => key);
|
|
367
|
+
const overFields = pipe(
|
|
368
|
+
fields.map(({ key, over }) => (over ? overProperty(key, over) : undefined)).filter(Boolean),
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
function editStringJSON(json, over) {
|
|
372
|
+
if (typeof json === 'string') {
|
|
373
|
+
const { indent, type } = detectIndent(json);
|
|
374
|
+
const endCharacters = json.slice(-1) === '\n' ? '\n' : '';
|
|
375
|
+
const newline = detectNewline(json);
|
|
376
|
+
json = JSON.parse(json);
|
|
377
|
+
|
|
378
|
+
let result =
|
|
379
|
+
JSON.stringify(over(json), null, type === 'tab' ? '\t' : indent) + endCharacters;
|
|
380
|
+
if (newline === '\r\n') {
|
|
381
|
+
result = result.replace(/\n/g, newline);
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return over(json);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const isPrivateKey = (key) => key[0] === '_';
|
|
390
|
+
const partition = (array, predicate) =>
|
|
391
|
+
array.reduce(
|
|
392
|
+
(result, value) => {
|
|
393
|
+
result[predicate(value) ? 0 : 1].push(value);
|
|
394
|
+
return result;
|
|
395
|
+
},
|
|
396
|
+
[[], []],
|
|
397
|
+
);
|
|
398
|
+
function sortPackageJson(jsonIsh, options = {}) {
|
|
399
|
+
return editStringJSON(
|
|
400
|
+
jsonIsh,
|
|
401
|
+
onObject((json) => {
|
|
402
|
+
let sortOrder = options.sortOrder || defaultSortOrder;
|
|
403
|
+
|
|
404
|
+
if (Array.isArray(sortOrder)) {
|
|
405
|
+
const keys = Object.keys(json);
|
|
406
|
+
const [privateKeys, publicKeys] = partition(keys, isPrivateKey);
|
|
407
|
+
sortOrder = [
|
|
408
|
+
...sortOrder,
|
|
409
|
+
...defaultSortOrder,
|
|
410
|
+
...publicKeys.sort(),
|
|
411
|
+
...privateKeys.sort(),
|
|
412
|
+
];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return overFields(sortObjectKeys(json, sortOrder), json);
|
|
416
|
+
}),
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export default sortPackageJson;
|
|
421
|
+
export { sortPackageJson, defaultSortOrder as sortOrder };
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jpp-toolkit/sort-package-json",
|
|
3
|
+
"version": "0.0.11",
|
|
4
|
+
"description": "Sort an Object or package.json based on the well-known package.json keys.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jpp",
|
|
7
|
+
"sort",
|
|
8
|
+
"package-json"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/jpapini/jpp-toolkit/tree/main/packages/sort-package-json#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/jpapini/jpp-toolkit/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/jpapini/jpp-toolkit.git",
|
|
17
|
+
"directory": "packages/sort-package-json"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "Julien Papini <julien.papini@gmail.com> (https://github.com/jpapini)",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./index.d.mts",
|
|
25
|
+
"default": "./index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"index.d.mts",
|
|
31
|
+
"index.mjs"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"detect-indent": "7.0.2",
|
|
35
|
+
"detect-newline": "4.0.1",
|
|
36
|
+
"git-hooks-list": "4.1.1",
|
|
37
|
+
"is-plain-obj": "4.1.0",
|
|
38
|
+
"semver": "7.7.3",
|
|
39
|
+
"sort-object-keys": "2.0.1"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": "24",
|
|
43
|
+
"pnpm": "10"
|
|
44
|
+
},
|
|
45
|
+
"volta": {
|
|
46
|
+
"extends": "../../package.json"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|