Skip to content

Commit

Permalink
Expand node:<script> and node --run comands
Browse files Browse the repository at this point in the history
Closes #475
  • Loading branch information
gustavohenke committed Aug 31, 2024
1 parent 1e882f1 commit e7a15fc
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 92 deletions.
38 changes: 21 additions & 17 deletions src/command-parser/expand-npm-shortcut.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,28 @@ it('returns same command if no npm: prefix is present', () => {
expect(parser.parse(commandInfo)).toBe(commandInfo);
});

for (const npmCmd of ['npm', 'yarn', 'pnpm', 'bun']) {
describe(`with ${npmCmd}: prefix`, () => {
it(`expands to "${npmCmd} run <script> <args>"`, () => {
const commandInfo = createCommandInfo(`${npmCmd}:foo -- bar`, 'echo');
expect(parser.parse(commandInfo)).toEqual({
...commandInfo,
name: 'echo',
command: `${npmCmd} run foo -- bar`,
});
describe.each([
['npm', 'run'],
['yarn', 'run'],
['pnpm', 'run'],
['bun', 'run'],
['node', '--run'],
])(`with %s: prefix`, (npmCmd, runCmd) => {
it(`expands to "${npmCmd} ${runCmd} <script> <args>"`, () => {
const commandInfo = createCommandInfo(`${npmCmd}:foo -- bar`, 'echo');
expect(parser.parse(commandInfo)).toEqual({
...commandInfo,
name: 'echo',
command: `${npmCmd} ${runCmd} foo -- bar`,
});
});

it('sets name to script name if none', () => {
const commandInfo = createCommandInfo(`${npmCmd}:foo -- bar`);
expect(parser.parse(commandInfo)).toEqual({
...commandInfo,
name: 'foo',
command: `${npmCmd} run foo -- bar`,
});
it('sets name to script name if none', () => {
const commandInfo = createCommandInfo(`${npmCmd}:foo -- bar`);
expect(parser.parse(commandInfo)).toEqual({
...commandInfo,
name: 'foo',
command: `${npmCmd} ${runCmd} foo -- bar`,
});
});
}
});
7 changes: 4 additions & 3 deletions src/command-parser/expand-npm-shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ import { CommandInfo } from '../command';
import { CommandParser } from './command-parser';

/**
* Expands commands prefixed with `npm:`, `yarn:`, `pnpm:`, or `bun:` into the full version `npm run <command>` and so on.
* Expands commands prefixed with `node:`, `npm:`, `yarn:`, `pnpm:`, or `bun:` into the full version `npm run <command>` and so on.
*/
export class ExpandNpmShortcut implements CommandParser {
parse(commandInfo: CommandInfo) {
const [, npmCmd, cmdName, args] =
commandInfo.command.match(/^(npm|yarn|pnpm|bun):(\S+)(.*)/) || [];
commandInfo.command.match(/^(node|npm|yarn|pnpm|bun):(\S+)(.*)/) || [];
if (!cmdName) {
return commandInfo;
}

const runCmd = npmCmd === 'node' ? '--run' : 'run';
return {
...commandInfo,
name: commandInfo.name || cmdName,
command: `${npmCmd} run ${cmdName}${args}`,
command: `${npmCmd} ${runCmd} ${cmdName}${args}`,
};
}
}
142 changes: 73 additions & 69 deletions src/command-parser/expand-npm-wildcard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,82 +67,86 @@ it('expands to nothing if no scripts exist in package.json', () => {
expect(parser.parse(createCommandInfo('npm run foo-*-baz qux'))).toEqual([]);
});

for (const npmCmd of ['npm', 'yarn', 'pnpm', 'bun']) {
describe(`with an ${npmCmd}: prefix`, () => {
it('expands to all scripts matching pattern', () => {
readPkg.mockReturnValue({
scripts: {
'foo-bar-baz': '',
'foo--baz': '',
},
});

expect(parser.parse(createCommandInfo(`${npmCmd} run foo-*-baz qux`))).toEqual([
{ name: 'bar', command: `${npmCmd} run foo-bar-baz qux` },
{ name: '', command: `${npmCmd} run foo--baz qux` },
]);
describe.each([
['npm', 'run'],
['yarn', 'run'],
['pnpm', 'run'],
['bun', 'run'],
['node', '--run'],
])(`with a %s: prefix`, (npmCmd, runCmd) => {
it('expands to all scripts matching pattern', () => {
readPkg.mockReturnValue({
scripts: {
'foo-bar-baz': '',
'foo--baz': '',
},
});

it('uses wildcard match of script as command name', () => {
readPkg.mockReturnValue({
scripts: {
'watch-js': '',
'watch-css': '',
},
});

expect(
parser.parse({
name: '',
command: `${npmCmd} run watch-*`,
}),
).toEqual([
{ name: 'js', command: `${npmCmd} run watch-js` },
{ name: 'css', command: `${npmCmd} run watch-css` },
]);
});
expect(parser.parse(createCommandInfo(`${npmCmd} ${runCmd} foo-*-baz qux`))).toEqual([
{ name: 'bar', command: `${npmCmd} ${runCmd} foo-bar-baz qux` },
{ name: '', command: `${npmCmd} ${runCmd} foo--baz qux` },
]);
});

it('uses existing command name as prefix to the wildcard match', () => {
readPkg.mockReturnValue({
scripts: {
'watch-js': '',
'watch-css': '',
},
});

expect(
parser.parse({
name: 'w:',
command: `${npmCmd} run watch-*`,
}),
).toEqual([
{ name: 'w:js', command: `${npmCmd} run watch-js` },
{ name: 'w:css', command: `${npmCmd} run watch-css` },
]);
it('uses wildcard match of script as command name', () => {
readPkg.mockReturnValue({
scripts: {
'watch-js': '',
'watch-css': '',
},
});

it('allows negation', () => {
readPkg.mockReturnValue({
scripts: {
'lint:js': '',
'lint:ts': '',
'lint:fix:js': '',
'lint:fix:ts': '',
},
});

expect(parser.parse(createCommandInfo(`${npmCmd} run lint:*(!fix)`))).toEqual([
{ name: 'js', command: `${npmCmd} run lint:js` },
{ name: 'ts', command: `${npmCmd} run lint:ts` },
]);
expect(
parser.parse({
name: '',
command: `${npmCmd} ${runCmd} watch-*`,
}),
).toEqual([
{ name: 'js', command: `${npmCmd} ${runCmd} watch-js` },
{ name: 'css', command: `${npmCmd} ${runCmd} watch-css` },
]);
});

it('uses existing command name as prefix to the wildcard match', () => {
readPkg.mockReturnValue({
scripts: {
'watch-js': '',
'watch-css': '',
},
});

it('caches scripts upon calls', () => {
readPkg.mockReturnValue({});
parser.parse(createCommandInfo(`${npmCmd} run foo-*-baz qux`));
parser.parse(createCommandInfo(`${npmCmd} run foo-*-baz qux`));
expect(
parser.parse({
name: 'w:',
command: `${npmCmd} ${runCmd} watch-*`,
}),
).toEqual([
{ name: 'w:js', command: `${npmCmd} ${runCmd} watch-js` },
{ name: 'w:css', command: `${npmCmd} ${runCmd} watch-css` },
]);
});

expect(readPkg).toHaveBeenCalledTimes(1);
it('allows negation', () => {
readPkg.mockReturnValue({
scripts: {
'lint:js': '',
'lint:ts': '',
'lint:fix:js': '',
'lint:fix:ts': '',
},
});

expect(parser.parse(createCommandInfo(`${npmCmd} ${runCmd} lint:*(!fix)`))).toEqual([
{ name: 'js', command: `${npmCmd} ${runCmd} lint:js` },
{ name: 'ts', command: `${npmCmd} ${runCmd} lint:ts` },
]);
});
}

it('caches scripts upon calls', () => {
readPkg.mockReturnValue({});
parser.parse(createCommandInfo(`${npmCmd} run foo-*-baz qux`));
parser.parse(createCommandInfo(`${npmCmd} run foo-*-baz qux`));

expect(readPkg).toHaveBeenCalledTimes(1);
});
});
6 changes: 3 additions & 3 deletions src/command-parser/expand-npm-wildcard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export class ExpandNpmWildcard implements CommandParser {
constructor(private readonly readPackage = ExpandNpmWildcard.readPackage) {}

parse(commandInfo: CommandInfo) {
const [, npmCmd, cmdName, args] =
commandInfo.command.match(/(npm|yarn|pnpm|bun) run (\S+)([^&]*)/) || [];
const [, npmCmd, runCmd, cmdName, args] =
commandInfo.command.match(/(node|npm|yarn|pnpm|bun) ((?:--)?run) (\S+)([^&]*)/) || [];
const wildcardPosition = (cmdName || '').indexOf('*');

// If the regex didn't match an npm script, or it has no wildcard,
Expand Down Expand Up @@ -63,7 +63,7 @@ export class ExpandNpmWildcard implements CommandParser {
if (match) {
return {
...commandInfo,
command: `${npmCmd} run ${script}${args}`,
command: `${npmCmd} ${runCmd} ${script}${args}`,
// Will use an empty command name if no prefix has been specified and
// the wildcard match is empty, e.g. if `npm:watch-*` matches `npm run watch-`.
name: prefix + match[1],
Expand Down

0 comments on commit e7a15fc

Please sign in to comment.