#node.js #npm
#npm #package.json #npm-скрипты
Вопрос:
У меня есть приведенные ниже скрипты в моем package.json:
"scripts": {
"vumper": "node node_modules/vumper/index.js",
"format": "prettier --single-quote -width=80 --write package.json"
},
Пакет ‘vumper’ принимает аргумент командной строки (например, ‘dv’). Что я хотел бы иметь возможность сделать, так это иметь команду, которая запускает оба из них последовательно.
По сути, я хотел бы иметь возможность запускать:
npm run vumber dv
и затем
npm run format
но в одной команде что-то вроде
npm run my-build dv
которые выполнили бы обе вышеуказанные команды, правильно приняв аргумент командной строки ‘dv’ и передав его первому исполнителю npm, выполняющему vumper. Возможно ли это?
Ответ №1:
Короткий ответ:
По сути, то, что вы хотите, — это иметь npm-скрипт примерно такого типа, в котором <arg-here>
предоставляется через CLI;
...
"scripts": {
"my-build": "npm run vumper <arg-here> amp;amp; npm run format",
...
},
...
Однако, к сожалению, npm не имеет встроенной функции для достижения этого.
Специальная опция npm --
(дополнительную информацию об этой опции см. в конце решения 1 ниже) может использоваться только для передачи аргумента в КОНЕЦ скрипта, но НЕ в СЕРЕДИНУ. Итак, если бы ваши две команды были в противоположном порядке, --
параметр можно было бы использовать следующим образом:
...
"scripts": {
"my-build": "npm run format amp;amp; npm run vumper --",
...
},
...
Чтобы преодолеть ограничение, заключающееся в отсутствии встроенной функции для передачи аргумента в СЕРЕДИНУ скрипта, рассмотрите следующие решения:
-
Решение, доступное только для Bash, см. в разделе «Решение 1».
-
Если требуется кроссплатформенная поддержка, следуйте решению, описанному в разделе «Решение 2».
Решение 1 — Bash (macOS / Linux / etc ..):
Настройте свой my-build
скрипт в scripts
разделе package.json для вызова функции оболочки Bash, как показано ниже:
package.json
...
"scripts": {
"my-build": "func() { npm run vumper "$1" amp;amp; npm run format; }; func",
"vumper": "node node_modules/vumper/index.js",
"format": "prettier --single-quote -width=80 --write package.json"
},
...
Объяснение:
Функция Bash с именем func
выполняет следующее:
- Сначала выполняется
npm run vumper <arg>
. При этом<arg>
будет аргументом оболочки, передаваемым через CLI. В скрипте используется ссылка на него$1
(т.Е. первый позиционный параметр / аргумент). - Впоследствии он запускает скрипт с именем
format
через командуnpm run format
.
Эти две npm run
команды связаны с помощью amp;amp;
оператора, поэтому вторая npm run format
команда будет выполняться только в том случае, если начальная npm run vumper <arg>
команда завершится успешно (т. Е. вернет 0
код выхода).
Запуск my-build
скрипта:
Для вызова my-build
через ваш CLI вам нужно выполнить:
npm run my-build -- dv
Примечание:
-
В этом случае завершающая
dv
часть является аргументом, который будет передан вашемуvumper
скрипту. -
Перед аргументом должна быть указана специальная опция
--
. В документах этот--
параметр описывается как:… Специальная опция
--
используетсяgetopt
для разграничения конца опций. npm передаст все аргументы после--
непосредственно вашему скрипту: … Аргументы будут переданы только скрипту, указанному послеnpm run
, а не какому-либо сценарию pre или post.
Решение 2 — Кроссплатформенность:
Для кроссплатформенного решения (которое успешно работает с Bash, командной строкой Windows / cmd.exe и PowerShell и т.д.), вам нужно будет использовать вспомогательный скрипт nodejs следующим образом.
run.js
Давайте назовем скрипт nodejs run.js и сохраните их в корневом каталоге проектов на том же уровне, что и package.json.
const execSync = require('child_process').execSync;
const arg = process.argv[2] || 'dv'; // Default value `dv` if no args provided via CLI.
execSync('npm run vumper ' arg, {stdio:[0, 1, 2]});
execSync('npm run format', {stdio:[0, 1, 2]});
package.json
Настройте свой my-build
скрипт для вызова run.js следующим образом:
...
"scripts": {
"my-build": "node run",
"vumper": "node node_modules/vumper/index.js",
"format": "prettier --single-quote -width=80 --write package.json"
},
...
Запуск my-build
скрипта:
Согласно решению 1, для вызова my-build
через ваш CLI вам необходимо выполнить:
npm run my-build -- dv
Объяснение:
-
run.js используется
process.argv
для получения аргумента, переданного через CLI (например,dv
). Если при запуске не указано никаких аргументов,npm run my-build
вdv
npm-скрипт передается значение по умолчанию (т.Е.vumper
). -
run.js также используется
child_process.execSync(...)
для выделения / вызова двухnpm run
команд.
Комментарии:
1.Нет ли пакета cli npm, который просто делал бы что-то вроде
const args = process.argv.slice(3);
const command = args.reduce(
(res, arg, index) => res.replace(new RegExp(`\$${ index 1 }`, 'g'), arg),
process.argv[2]
);
const execSync = require('child_process').execSync;
execSync(command, { stdio: [0, 1, 2] });
?. Таким образом, я могу просто обернуть любой скрипт с помощью » и добавить к нему такой префикс cli и запустить любой скрипт с упорядоченными аргументами…
Ответ №2:
В Npm теперь есть встроенная опция для передачи аргументов cli непосредственно скриптам. Аргументы cli хранятся в переменных среды с префиксом npm_config_<flagname>
, и для них требуется очень строгий синтаксис с формой --<flagname>=<flagvalue>
.
Пример:
"my-build": "npm run vumper %npm_config_myflag% amp;amp; npm run format",
В терминале запустите npm run my-build --myflag=my_value
для выполнения npm run vumper my_value amp;amp; npm run format
.
Примечание:
Чтобы ссылаться на переменную среды в скрипте npm, вы должны использовать синтаксис, зависящий от платформы, т.Е. %npm_config_myflag%
в Windows или $npm_config_myflag
в Linux.
Обновить:
Чтобы избежать риска конфликта с переменными npm_config, используемыми для настройки самого npm, просто добавляйте к своим аргументам уникальный префикс, например, имя вашего приложения.
Потенциальный конфликт является очень распространенной проблемой, которая применима во многих контекстах: любое приложение может использовать переменные среды, уже используемые другими приложениями; по этой причине переменные среды обычно имеют префикс имени приложения (например, NVM_HOME, JAVA_HOME). Но этот потенциальный конфликт не является веской причиной избегать использования переменных среды. То же самое, на мой взгляд, относится и к переменным npm params / npm_config env. В документе ничего не говорится о риске конфликтов, подразумевая, что, я полагаю, ими следует управлять как обычно.
Комментарии:
1. Для меня ключевым фактором была специфика платформы. Я пытался
$npm_config_myflag
работать с Windows и сходил с ума2. npm также создает свои собственные переменные env с именем
npm_config_*
для внутренних целей. Обязательно выберите имя для флага / опции (например,--myflag=*
), которое не переопределяет внутренне используемый, поскольку это может привести к нежелательным результатам. Вы можетеcd
перейти в каталог вашего проекта и запуститьnpm run env
, чтобы получить список переменных env, добавляемых npm. Например. передача--tag=foo
не является хорошей идеей, потому что npm добавляетnpm_config_tag
переменную env (ее значение обычно устанавливается равнымlatest
). Используя этот метод, неясно, какие внутренниеnpm_config_*
переменные npm могут быть добавлены в будущем, которые вы в конечном итоге можете переопределить.3. Вы можете обратиться к этому подходу, но использование этого подхода может быть опасным, как объяснил @RobC в комментарии. Таким образом, не в отношении решения этого вопроса, но в целом, если чьим-то требованием является добавление аргумента в конце команды, тогда не используйте подход с переменной среды, в таком случае он / она может использовать npm run <имя_скрипта> — <аргумент>.
Ответ №3:
Мой предпочтительный метод — использование переменных окружения:
{
"scripts": {
"ncc-build": "ncc build $ACTION/src/index.ts -o $ACTION/dist",
"build:pr-changelog": "ACTION=pr-changelog npm run ncc-build",
}
}
Это должно работать в системах UNIX. Однако я не уверен в совместимости Windows platfrom.
Комментарии:
1. да, это не работает на платформе Windows
2. для Windows вы бы так и сделали
set ACTION=...
, но если вы хотите использовать на любой платформе, используйте пакет cross-env
Ответ №4:
другой подход для этого — проникнуть очень глубоко в вашу цепочку зависимостей:
раздел скриптов npm:
"test:local": "cross-env-shell UPDATE_BASELINE=false UPDATE_MODULE=%npm_config_vizdifsingle% run-p koa:ci wdio:local",
"test:remote": "cross-env-shell UPDATE_BASELINE=false UPDATE_MODULE=%npm_config_vizdifsingle% run-p localtunnel:start koa:ci wdio:remote"
используя crossenv и размещение значений npm, вы можете передавать аргументы в env.args
вот так:
npm run test:local --vizdifsingle=some,value,or,values
они будут доступны вам в
process.env.npm_config_update_module