Skip to content

Commit e6052c4

Browse files
Yaman-kumarsahufiskeri529015
authored
fix: improve-scripts-sorting (#376)
Co-authored-by: fisker Cheung <lionkay@gmail.com> Co-authored-by: i529015 <yaman.kumar.sahu@sap.com>
1 parent f94ab01 commit e6052c4

File tree

2 files changed

+143
-7
lines changed

2 files changed

+143
-7
lines changed

index.js

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,43 @@ const hasSequentialScript = (packageJson) => {
273273
return scripts.some((script) => isSequentialScript(script))
274274
}
275275

276+
function sortScriptNames(keys, prefix = '') {
277+
const groupMap = new Map()
278+
for (const key of keys) {
279+
const rest = prefix ? key.slice(prefix.length + 1) : key
280+
const idx = rest.indexOf(':')
281+
if (idx !== -1) {
282+
const base = key.slice(0, (prefix ? prefix.length + 1 : 0) + idx)
283+
if (!groupMap.has(base)) groupMap.set(base, [])
284+
groupMap.get(base).push(key)
285+
} else {
286+
if (!groupMap.has(key)) groupMap.set(key, [])
287+
groupMap.get(key).push(key)
288+
}
289+
}
290+
return Array.from(groupMap.keys())
291+
.sort()
292+
.flatMap((groupKey) => {
293+
const children = groupMap.get(groupKey)
294+
if (
295+
children.length > 1 &&
296+
children.some((k) => k !== groupKey && k.startsWith(groupKey + ':'))
297+
) {
298+
const direct = children
299+
.filter((k) => k === groupKey || !k.startsWith(groupKey + ':'))
300+
.sort()
301+
const nested = children.filter((k) => k.startsWith(groupKey + ':'))
302+
return [...direct, ...sortScriptNames(nested, groupKey)]
303+
}
304+
return children.sort()
305+
})
306+
}
307+
276308
const sortScripts = onObject((scripts, packageJson) => {
277-
const names = Object.keys(scripts)
309+
let names = Object.keys(scripts)
278310
const prefixable = new Set()
279311

280-
const keys = names.map((name) => {
312+
names = names.map((name) => {
281313
const omitted = name.replace(/^(?:pre|post)/, '')
282314
if (defaultNpmScripts.has(omitted) || names.includes(omitted)) {
283315
prefixable.add(omitted)
@@ -287,14 +319,12 @@ const sortScripts = onObject((scripts, packageJson) => {
287319
})
288320

289321
if (!hasSequentialScript(packageJson)) {
290-
keys.sort()
322+
names = sortScriptNames(names)
291323
}
292-
293-
const order = keys.flatMap((key) =>
324+
names = names.flatMap((key) =>
294325
prefixable.has(key) ? [`pre${key}`, key, `post${key}`] : [key],
295326
)
296-
297-
return sortObjectKeys(scripts, order)
327+
return sortObjectKeys(scripts, names)
298328
})
299329

300330
/*

tests/scripts.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,109 @@ for (const field of ['scripts', 'betterScripts']) {
225225
},
226226
)
227227
}
228+
229+
test('scripts: group base and colon scripts together, do not split with unrelated', (t) => {
230+
const input = {
231+
scripts: {
232+
test: 'run-s test:a test:b',
233+
'test:a': 'foo',
234+
'test:b': 'bar',
235+
'test-coverage': 'c8 node --run test',
236+
},
237+
}
238+
const sorted = sortPackageJson(input)
239+
t.deepEqual(Object.keys(sorted.scripts), [
240+
'test',
241+
'test:a',
242+
'test:b',
243+
'test-coverage',
244+
])
245+
})
246+
247+
test('scripts: group scripts with multiple colons', (t) => {
248+
const input = {
249+
scripts: {
250+
test: 'run-s test:a test:b',
251+
'test:a': 'foo',
252+
'test:b': 'bar',
253+
'pretest:a': 'foo',
254+
'posttest:a': 'foo',
255+
'pretest:a:a': 'foo',
256+
'posttest:a:a': 'foo',
257+
'test:a:a': 'foofoo',
258+
'test:a:b': 'foobar',
259+
'pretest:ab': 'foobar',
260+
'test:ab': 'foobar',
261+
'test:a-coverage': 'foobar',
262+
'test:b:a': 'barfoo',
263+
'test:b:b': 'barbar',
264+
'test-coverage': 'c8 node --run test',
265+
},
266+
}
267+
const sorted = sortPackageJson(input)
268+
t.deepEqual(Object.keys(sorted.scripts), [
269+
'test',
270+
'pretest:a',
271+
'test:a',
272+
'posttest:a',
273+
'pretest:a:a',
274+
'test:a:a',
275+
'posttest:a:a',
276+
'test:a:b',
277+
'test:a-coverage',
278+
'pretest:ab',
279+
'test:ab',
280+
'test:b',
281+
'test:b:a',
282+
'test:b:b',
283+
'test-coverage',
284+
])
285+
})
286+
287+
test('scripts: nested production and format variants are grouped and sorted', (t) => {
288+
const input = {
289+
scripts: {
290+
test: 'echo',
291+
'test:a': 'echo',
292+
'test:b': 'echo',
293+
'test:ab': 'echo',
294+
'test-coverage': 'echo',
295+
'test:production': 'echo',
296+
'test:production:a': 'echo',
297+
'test:production:b': 'echo',
298+
'test:production-coverage': 'echo',
299+
'test:production2': 'echo',
300+
'test:production$2': 'echo',
301+
'test:production:cjs': 'echo',
302+
'test:production:cjs:a': 'echo',
303+
'test:production:cjs:b': 'echo',
304+
'test:production:cjs-coverage': 'echo',
305+
'test:production:mjs': 'echo',
306+
'test:production:mjs:a': 'echo',
307+
'test:production:mjs:b': 'echo',
308+
'test:production:mjs-coverage': 'echo',
309+
},
310+
}
311+
const sorted = sortPackageJson(input)
312+
t.deepEqual(Object.keys(sorted.scripts), [
313+
'test',
314+
'test:a',
315+
'test:ab',
316+
'test:b',
317+
'test:production',
318+
'test:production:a',
319+
'test:production:b',
320+
'test:production:cjs',
321+
'test:production:cjs:a',
322+
'test:production:cjs:b',
323+
'test:production:cjs-coverage',
324+
'test:production:mjs',
325+
'test:production:mjs:a',
326+
'test:production:mjs:b',
327+
'test:production:mjs-coverage',
328+
'test:production$2',
329+
'test:production-coverage',
330+
'test:production2',
331+
'test-coverage',
332+
])
333+
})

0 commit comments

Comments
 (0)