just-scripts 2.5.0 → 2.6.0

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.
Files changed (45) hide show
  1. package/CHANGELOG.json +48 -1
  2. package/CHANGELOG.md +18 -2
  3. package/lib/tasks/eslintTask.d.ts.map +1 -1
  4. package/lib/tasks/eslintTask.js +4 -5
  5. package/lib/tasks/eslintTask.js.map +1 -1
  6. package/lib/tasks/jestTask.d.ts +8 -0
  7. package/lib/tasks/jestTask.d.ts.map +1 -1
  8. package/lib/tasks/jestTask.js +8 -9
  9. package/lib/tasks/jestTask.js.map +1 -1
  10. package/lib/tasks/nodeExecTask.d.ts +22 -8
  11. package/lib/tasks/nodeExecTask.d.ts.map +1 -1
  12. package/lib/tasks/nodeExecTask.js +16 -10
  13. package/lib/tasks/nodeExecTask.js.map +1 -1
  14. package/lib/tasks/prettierTask.d.ts +1 -0
  15. package/lib/tasks/prettierTask.d.ts.map +1 -1
  16. package/lib/tasks/prettierTask.js +7 -21
  17. package/lib/tasks/prettierTask.js.map +1 -1
  18. package/lib/tasks/tscTask.d.ts.map +1 -1
  19. package/lib/tasks/tscTask.js +20 -38
  20. package/lib/tasks/tscTask.js.map +1 -1
  21. package/lib/tasks/tslintTask.d.ts.map +1 -1
  22. package/lib/tasks/tslintTask.js +7 -8
  23. package/lib/tasks/tslintTask.js.map +1 -1
  24. package/lib/tasks/webpackCliInitTask.js +2 -1
  25. package/lib/tasks/webpackCliInitTask.js.map +1 -1
  26. package/lib/tasks/webpackCliTask.js +1 -1
  27. package/lib/tasks/webpackCliTask.js.map +1 -1
  28. package/lib/tasks/webpackDevServerTask.d.ts.map +1 -1
  29. package/lib/tasks/webpackDevServerTask.js +1 -1
  30. package/lib/tasks/webpackDevServerTask.js.map +1 -1
  31. package/lib/utils/exec.d.ts +20 -31
  32. package/lib/utils/exec.d.ts.map +1 -1
  33. package/lib/utils/exec.js +53 -61
  34. package/lib/utils/exec.js.map +1 -1
  35. package/package.json +4 -2
  36. package/src/tasks/eslintTask.ts +7 -6
  37. package/src/tasks/jestTask.ts +18 -9
  38. package/src/tasks/nodeExecTask.ts +33 -17
  39. package/src/tasks/prettierTask.ts +9 -26
  40. package/src/tasks/tscTask.ts +21 -44
  41. package/src/tasks/tslintTask.ts +8 -7
  42. package/src/tasks/webpackCliInitTask.ts +1 -1
  43. package/src/tasks/webpackCliTask.ts +2 -2
  44. package/src/tasks/webpackDevServerTask.ts +3 -3
  45. package/src/utils/exec.ts +52 -66
@@ -1 +1 @@
1
- {"version":3,"file":"webpackDevServerTask.d.ts","sourceRoot":"","sources":["../../src/tasks/webpackDevServerTask.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAA+B,YAAY,EAAE,MAAM,WAAW,CAAC;AAGtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAKzD,MAAM,WAAW,2BAA4B,SAAQ,qBAAqB,EAAE,aAAa;IACvF;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;OAEG;IACH,IAAI,CAAC,EAAE,YAAY,GAAG,aAAa,CAAC;IAEpC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;OAEG;IACH,GAAG,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAE5C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,2BAAgC,GAAG,YAAY,CAiD5F"}
1
+ {"version":3,"file":"webpackDevServerTask.d.ts","sourceRoot":"","sources":["../../src/tasks/webpackDevServerTask.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAuB,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAKzD,MAAM,WAAW,2BAA4B,SAAQ,qBAAqB,EAAE,aAAa;IACvF;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;OAEG;IACH,IAAI,CAAC,EAAE,YAAY,GAAG,aAAa,CAAC;IAEpC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;OAEG;IACH,GAAG,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAE5C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,2BAAgC,GAAG,YAAY,CAiD5F"}
@@ -42,7 +42,7 @@ function webpackDevServerTask(options = {}) {
42
42
  if (options.webpackCliArgs) {
43
43
  args = [...args, ...options.webpackCliArgs];
44
44
  }
45
- just_task_1.logger.info(process.execPath, (0, utils_1.encodeArgs)(args).join(' '));
45
+ (0, utils_1.logNodeCommand)(args);
46
46
  return (0, utils_1.spawn)(process.execPath, args, { stdio: 'inherit', env: options.env });
47
47
  };
48
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"webpackDevServerTask.js","sourceRoot":"","sources":["../../src/tasks/webpackDevServerTask.ts"],"names":[],"mappings":";;;AAEA,oCAA6C;AAC7C,yCAAsE;AACtE,yBAAyB;AACzB,6BAA6B;AAE7B,6DAA0D;AAC1D,oEAAiE;AACjE,iCAAiC;AAwCjC,SAAgB,oBAAoB,CAAC,UAAuC,EAAE;IAC5E,MAAM,UAAU,GACd,OAAO,IAAI,OAAO,CAAC,MAAM;QACvB,CAAC,CAAC,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC,CAAC,IAAA,qCAAiB,EAAC,yBAAyB,EAAE,mBAAmB,CAAC,CAAC;IAExE,kGAAkG;IAClG,MAAM,cAAc,GAAG,IAAA,mBAAO,EAAC,0BAA0B,CAAC,CAAC;IAE3D,IAAI,CAAC,cAAc,EAAE;QACnB,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;KAChG;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAEvF,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAE/D,MAAM,YAAY,GAAG,eAAe;QAClC,CAAC,CAAC,oEAAoE;YACpE,CAAC,IAAA,mBAAO,EAAC,wBAAwB,CAAE,EAAE,OAAO,CAAC;QAC/C,CAAC,CAAC,oEAAoE;YACpE,CAAC,IAAA,mBAAO,EAAC,8CAA8C,CAAE,CAAC,CAAC;IAE/D,OAAO,SAAS,gBAAgB;QAC9B,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC;QAE1D,IAAI,UAAU,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;YAC3C,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YACzC,OAAO,CAAC,GAAG,GAAG;gBACZ,GAAG,OAAO,CAAC,GAAG;gBACd,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAA,2BAAY,EAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;aACzF,CAAC;SACH;QAED,IAAI,OAAO,CAAC,IAAI,EAAE;YAChB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SACrB;QAED,IAAI,OAAO,CAAC,IAAI,EAAE;YAChB,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;SAC1C;QAED,IAAI,OAAO,CAAC,cAAc,EAAE;YAC1B,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;SAC7C;QAED,kBAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAA,kBAAU,EAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,OAAO,IAAA,aAAK,EAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC;AAjDD,oDAiDC"}
1
+ {"version":3,"file":"webpackDevServerTask.js","sourceRoot":"","sources":["../../src/tasks/webpackDevServerTask.ts"],"names":[],"mappings":";;;AAEA,oCAAiD;AACjD,yCAA8D;AAC9D,yBAAyB;AACzB,6BAA6B;AAE7B,6DAA0D;AAC1D,oEAAiE;AACjE,iCAAiC;AAwCjC,SAAgB,oBAAoB,CAAC,UAAuC,EAAE;IAC5E,MAAM,UAAU,GACd,OAAO,IAAI,OAAO,CAAC,MAAM;QACvB,CAAC,CAAC,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC,CAAC,IAAA,qCAAiB,EAAC,yBAAyB,EAAE,mBAAmB,CAAC,CAAC;IAExE,kGAAkG;IAClG,MAAM,cAAc,GAAG,IAAA,mBAAO,EAAC,0BAA0B,CAAC,CAAC;IAE3D,IAAI,CAAC,cAAc,EAAE;QACnB,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;KAChG;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAEvF,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAE/D,MAAM,YAAY,GAAG,eAAe;QAClC,CAAC,CAAC,oEAAoE;YACpE,CAAC,IAAA,mBAAO,EAAC,wBAAwB,CAAE,EAAE,OAAO,CAAC;QAC/C,CAAC,CAAC,oEAAoE;YACpE,CAAC,IAAA,mBAAO,EAAC,8CAA8C,CAAE,CAAC,CAAC;IAE/D,OAAO,SAAS,gBAAgB;QAC9B,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC;QAE1D,IAAI,UAAU,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;YAC3C,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YACzC,OAAO,CAAC,GAAG,GAAG;gBACZ,GAAG,OAAO,CAAC,GAAG;gBACd,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAA,2BAAY,EAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;aACzF,CAAC;SACH;QAED,IAAI,OAAO,CAAC,IAAI,EAAE;YAChB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SACrB;QAED,IAAI,OAAO,CAAC,IAAI,EAAE;YAChB,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;SAC1C;QAED,IAAI,OAAO,CAAC,cAAc,EAAE;YAC1B,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;SAC7C;QAED,IAAA,sBAAc,EAAC,IAAI,CAAC,CAAC;QACrB,OAAO,IAAA,aAAK,EAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC;AAjDD,oDAiDC"}
@@ -1,45 +1,34 @@
1
1
  /// <reference types="node" />
2
2
  import * as cp from 'child_process';
3
- export interface ExecError extends cp.ExecException {
4
- stdout?: string;
5
- stderr?: string;
6
- }
7
3
  /**
8
- * Execute a command.
9
- *
10
- * @param cmd Command to execute
11
- * @param opts Normal exec options plus stdout/stderr for piping output. Can pass `process` for this param.
12
- * @returns Promise which will settle when the command completes. If output was not piped, it will be
13
- * returned as the promise's value. If the promise was rejected, the error will be of type `ExecError`.
14
- */
15
- export declare function exec(cmd: string, opts?: cp.ExecOptions & {
16
- stdout?: NodeJS.WritableStream;
17
- stderr?: NodeJS.WritableStream;
18
- }): Promise<string | undefined>;
19
- /**
20
- * Encode args for a shell command.
21
- * @param cmdArgs Args to encode
22
- * @returns Encoded args
4
+ * @deprecated This prevents issues from spaces in args, but does NOT escape shell metacharacters.
5
+ * For full escaping, consider a library such as `shell-quote` instead. (Note that `spawn` and tasks
6
+ * from this package now use `cross-spawn` which escapes spaces internally.)
23
7
  */
24
8
  export declare function encodeArgs(cmdArgs: string[]): string[];
25
9
  /**
26
- * Execute a command synchronously.
27
- *
28
- * @param cmd Command to execute
29
- * @param cwd Working directory in which to run the command (default: `process.cwd()`)
30
- * @param returnOutput If true, return the command's output. If false/unspecified,
31
- * inherit stdio from the parent process (so the child's output goes to the console).
32
- * @returns If `returnOutput` is true, returns the command's output. Otherwise returns undefined.
10
+ * Log `Running: <process.execPath> <...args>`
33
11
  */
34
- export declare function execSync(cmd: string, cwd?: string, returnOutput?: boolean): string | undefined;
12
+ export declare function logNodeCommand(...args: (string | string[])[]): void;
35
13
  /**
36
- * Execute a command in a new process.
14
+ * Execute a command in a new process. Uses `cross-spawn` to avoid issues with spaces in arguments,
15
+ * but does not do any additional escaping. (For further enhancements, consider using the `execa`
16
+ * library instead.)
17
+ *
18
+ * **WARNING: If the `shell` option is enabled, do not pass unsanitized user input to this function.
19
+ * Any input containing shell metacharacters may be used to trigger arbitrary command execution.**
37
20
  *
38
21
  * @param cmd Command to execute
39
- * @param args Args for the command
40
- * @param opts Normal spawn options plus stdout/stderr for piping output. Can pass `process` for this param.
22
+ * @param args Args for the command. Quoting spaces is handled internally by `cross-spawn`.
23
+ * @param opts Normal spawn options plus stdout/stderr for piping output. (To inherit stdio from the
24
+ * parent process, just use `stdio: 'inherit'` instead.)
25
+ *
41
26
  * @returns Promise which will settle when the command completes. If the promise is rejected, the error will
42
- * include the child process's exit code.
27
+ * include the child process's exit code (`error.code`) or signal (`error.signal`) if relevant.
28
+ * The returned promise also has a `child` property with the spawned `ChildProcess` instance.
29
+ *
30
+ * @deprecated This function is not recommended for use outside the `just-scripts` package.
31
+ * Instead, consider a more mature, purpose-specific library such as `execa` or `nano-spawn`.
43
32
  */
44
33
  export declare function spawn(cmd: string, args?: ReadonlyArray<string>, opts?: cp.SpawnOptions & {
45
34
  stdout?: NodeJS.WritableStream;
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,MAAM,eAAe,CAAC;AAGpC,MAAM,WAAW,SAAU,SAAQ,EAAE,CAAC,aAAa;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,EAAE,CAAC,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;CAAO,GAC7F,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoB7B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAetD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAY9F;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,aAAa,CAAC,MAAM,CAAM,EAChC,IAAI,GAAE,EAAE,CAAC,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;CAAO,GAC9F,OAAO,CAAC,IAAI,CAAC,CAwBf"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,MAAM,eAAe,CAAC;AAMpC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAEtD;AAyBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,CAEnE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,aAAa,CAAC,MAAM,CAAM,EAChC,IAAI,GAAE,EAAE,CAAC,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;CAAO,GAC9F,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
package/lib/utils/exec.js CHANGED
@@ -1,45 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.spawn = exports.execSync = exports.encodeArgs = exports.exec = void 0;
4
- const cp = require("child_process");
5
- const path_1 = require("path");
6
- const SEPARATOR = process.platform === 'win32' ? ';' : ':';
3
+ exports.spawn = exports.logNodeCommand = exports.encodeArgs = void 0;
4
+ const cross_spawn_1 = require("cross-spawn");
5
+ const just_task_1 = require("just-task");
6
+ // `exec` and `execSync` were removed due to security issues (keeping filename for history)
7
7
  /**
8
- * Execute a command.
9
- *
10
- * @param cmd Command to execute
11
- * @param opts Normal exec options plus stdout/stderr for piping output. Can pass `process` for this param.
12
- * @returns Promise which will settle when the command completes. If output was not piped, it will be
13
- * returned as the promise's value. If the promise was rejected, the error will be of type `ExecError`.
8
+ * @deprecated This prevents issues from spaces in args, but does NOT escape shell metacharacters.
9
+ * For full escaping, consider a library such as `shell-quote` instead. (Note that `spawn` and tasks
10
+ * from this package now use `cross-spawn` which escapes spaces internally.)
14
11
  */
15
- function exec(cmd, opts = {}) {
16
- return new Promise((resolve, reject) => {
17
- var _a, _b;
18
- const child = cp.exec(cmd, opts, (error, stdout, stderr) => {
19
- if (error) {
20
- error.stdout = stdout;
21
- error.stderr = stderr;
22
- reject(error);
23
- }
24
- else {
25
- resolve(stdout);
26
- }
27
- });
28
- if (opts.stdout) {
29
- (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.pipe(opts.stdout);
30
- }
31
- if (opts.stderr) {
32
- (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.pipe(opts.stderr);
33
- }
34
- });
12
+ function encodeArgs(cmdArgs) {
13
+ return quoteSpaces(cmdArgs);
35
14
  }
36
- exports.exec = exec;
15
+ exports.encodeArgs = encodeArgs;
37
16
  /**
38
- * Encode args for a shell command.
39
- * @param cmdArgs Args to encode
40
- * @returns Encoded args
17
+ * Quote arguments containing spaces. Note that this does NOT do any other escaping!
18
+ * For more complete escaping, consider a library such as `shell-quote`, or use safer APIs which
19
+ * don't require escaping. (Note that `spawn` from this package now uses `cross-spawn` which
20
+ * escapes spaces internally.)
41
21
  */
42
- function encodeArgs(cmdArgs) {
22
+ function quoteSpaces(cmdArgs) {
43
23
  // Taken from https://github.com/xxorax/node-shell-escape/blob/master/shell-escape.js
44
24
  // However, we needed to use double quotes because that's the norm in more platforms
45
25
  if (!cmdArgs) {
@@ -53,42 +33,46 @@ function encodeArgs(cmdArgs) {
53
33
  return arg;
54
34
  });
55
35
  }
56
- exports.encodeArgs = encodeArgs;
57
36
  /**
58
- * Execute a command synchronously.
59
- *
60
- * @param cmd Command to execute
61
- * @param cwd Working directory in which to run the command (default: `process.cwd()`)
62
- * @param returnOutput If true, return the command's output. If false/unspecified,
63
- * inherit stdio from the parent process (so the child's output goes to the console).
64
- * @returns If `returnOutput` is true, returns the command's output. Otherwise returns undefined.
37
+ * Log `Running: <process.execPath> <...args>`
65
38
  */
66
- function execSync(cmd, cwd, returnOutput) {
67
- cwd = cwd || process.cwd();
68
- const env = { ...process.env };
69
- env.PATH = (0, path_1.resolve)('./node_modules/.bin') + SEPARATOR + env.PATH;
70
- const output = cp.execSync(cmd, {
71
- cwd,
72
- env,
73
- stdio: returnOutput ? undefined : 'inherit',
74
- });
75
- return returnOutput ? (output || '').toString('utf8') : undefined;
39
+ function logNodeCommand(...args) {
40
+ just_task_1.logger.info(`Running: ${process.execPath} ${quoteSpaces(args.flat()).join(' ')}`);
76
41
  }
77
- exports.execSync = execSync;
42
+ exports.logNodeCommand = logNodeCommand;
78
43
  /**
79
- * Execute a command in a new process.
44
+ * Execute a command in a new process. Uses `cross-spawn` to avoid issues with spaces in arguments,
45
+ * but does not do any additional escaping. (For further enhancements, consider using the `execa`
46
+ * library instead.)
47
+ *
48
+ * **WARNING: If the `shell` option is enabled, do not pass unsanitized user input to this function.
49
+ * Any input containing shell metacharacters may be used to trigger arbitrary command execution.**
80
50
  *
81
51
  * @param cmd Command to execute
82
- * @param args Args for the command
83
- * @param opts Normal spawn options plus stdout/stderr for piping output. Can pass `process` for this param.
52
+ * @param args Args for the command. Quoting spaces is handled internally by `cross-spawn`.
53
+ * @param opts Normal spawn options plus stdout/stderr for piping output. (To inherit stdio from the
54
+ * parent process, just use `stdio: 'inherit'` instead.)
55
+ *
84
56
  * @returns Promise which will settle when the command completes. If the promise is rejected, the error will
85
- * include the child process's exit code.
57
+ * include the child process's exit code (`error.code`) or signal (`error.signal`) if relevant.
58
+ * The returned promise also has a `child` property with the spawned `ChildProcess` instance.
59
+ *
60
+ * @deprecated This function is not recommended for use outside the `just-scripts` package.
61
+ * Instead, consider a more mature, purpose-specific library such as `execa` or `nano-spawn`.
86
62
  */
87
63
  function spawn(cmd, args = [], opts = {}) {
88
64
  return new Promise((resolve, reject) => {
89
65
  var _a, _b;
90
- const child = cp.spawn(cmd, args, opts);
91
- child.on('exit', (code, signal) => {
66
+ let child;
67
+ try {
68
+ child = (0, cross_spawn_1.spawn)(cmd, args, opts);
69
+ }
70
+ catch (error) {
71
+ reject(error);
72
+ return;
73
+ }
74
+ const onExit = (code, signal) => {
75
+ child.off('error', onError);
92
76
  if (code) {
93
77
  const error = new Error('Command failed: ' + [cmd, ...args].join(' '));
94
78
  error.code = code;
@@ -102,7 +86,15 @@ function spawn(cmd, args = [], opts = {}) {
102
86
  else {
103
87
  resolve();
104
88
  }
105
- });
89
+ };
90
+ // Some error circumstances may fire 'error' rather than 'exit'
91
+ // https://nodejs.org/docs/latest/api/child_process.html#event-error
92
+ const onError = (error) => {
93
+ reject(error);
94
+ child.off('exit', onExit);
95
+ };
96
+ child.on('exit', onExit);
97
+ child.on('error', onError);
106
98
  if (opts.stdout) {
107
99
  (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.pipe(opts.stdout);
108
100
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "just-scripts",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Just Stack Scripts",
5
5
  "keywords": [],
6
6
  "repository": {
@@ -28,10 +28,11 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "chalk": "^4.0.0",
31
+ "cross-spawn": "^7.0.6",
31
32
  "diff-match-patch": "1.0.5",
32
33
  "fs-extra": "^11.0.0",
33
34
  "glob": "^7.1.3",
34
- "just-task": ">=1.13.0 <2.0.0",
35
+ "just-task": "^1.13.0",
35
36
  "prompts": "^2.4.0",
36
37
  "run-parallel-limit": "^1.0.6",
37
38
  "semver": "^7.0.0",
@@ -39,6 +40,7 @@
39
40
  "webpack-merge": "^5.7.3"
40
41
  },
41
42
  "devDependencies": {
43
+ "@types/cross-spawn": "^6.0.6",
42
44
  "@types/diff-match-patch": "^1.0.32",
43
45
  "@types/glob": "^7.2.0",
44
46
  "@types/prompts": "^2.4.2",
@@ -1,5 +1,5 @@
1
- import { resolve, logger, resolveCwd, TaskFunction } from 'just-task';
2
- import { encodeArgs, spawn } from '../utils';
1
+ import { resolve, resolveCwd, TaskFunction } from 'just-task';
2
+ import { logNodeCommand, spawn } from '../utils';
3
3
  import * as fs from 'fs';
4
4
 
5
5
  /**
@@ -70,7 +70,7 @@ export function eslintTask(options: EsLintTaskOptions = {}): TaskFunction {
70
70
  eslintCmd,
71
71
  ...(files ? files : ['.']),
72
72
  ...['--ext', extensions ? extensions : '.js,.jsx,.ts,.tsx'],
73
- ...(noEslintRc ? '--no-eslintrc' : []),
73
+ ...(noEslintRc ? ['--no-eslintrc'] : []),
74
74
  ...(eslintConfigPath ? ['--config', eslintConfigPath] : []),
75
75
  ...(eslintIgnorePath ? ['--ignore-path', eslintIgnorePath] : []),
76
76
  ...(resolvePluginsPath ? ['--resolve-plugins-relative-to', resolvePluginsPath] : []),
@@ -95,10 +95,11 @@ export function eslintTask(options: EsLintTaskOptions = {}): TaskFunction {
95
95
  env.ESLINT_USE_FLAT_CONFIG = JSON.stringify(useFlatConfig);
96
96
  }
97
97
 
98
- logger.info(encodeArgs(eslintArgs).join(' '));
98
+ logNodeCommand(eslintArgs);
99
99
  return spawn(process.execPath, eslintArgs, { stdio: 'inherit', env });
100
- } else {
101
- return Promise.resolve();
102
100
  }
101
+
102
+ // undertaker apparently requires returning a promise, async function, or function that calls done()
103
+ return Promise.resolve();
103
104
  };
104
105
  }
@@ -1,5 +1,5 @@
1
1
  import { resolve, logger, resolveCwd, TaskFunction, argv } from 'just-task';
2
- import { spawn, encodeArgs, readPackageJson } from '../utils';
2
+ import { spawn, readPackageJson, logNodeCommand } from '../utils';
3
3
  import { existsSync } from 'fs';
4
4
  import * as supportsColor from 'supports-color';
5
5
 
@@ -14,7 +14,15 @@ export interface JestTaskOptions {
14
14
  passWithNoTests?: boolean;
15
15
  clearCache?: boolean;
16
16
  silent?: boolean;
17
+ /**
18
+ * This is not available in jest 30+
19
+ * Consider updating to jest 30 and using testPathPatterns (plural) instead.
20
+ */
17
21
  testPathPattern?: string;
22
+ /**
23
+ * Compatible with jest 30+ only
24
+ */
25
+ testPathPatterns?: string;
18
26
  testNamePattern?: string;
19
27
  // The maximum number of workers to use in jest for parallel test execution
20
28
  maxWorkers?: number;
@@ -54,7 +62,6 @@ export function jestTask(options: JestTaskOptions = {}): TaskFunction {
54
62
 
55
63
  if ((configFileExists || packageConfigExists) && jestCmd) {
56
64
  logger.info(`Running Jest`);
57
- const cmd = process.execPath;
58
65
 
59
66
  const positional = argv()._.slice(1);
60
67
 
@@ -71,20 +78,22 @@ export function jestTask(options: JestTaskOptions = {}): TaskFunction {
71
78
  ...(options.watch ? ['--watch'] : []),
72
79
  ...(options.silent ? ['--silent'] : []),
73
80
  ...(options.testPathPattern ? ['--testPathPattern', options.testPathPattern] : []),
81
+ ...(options.testPathPatterns ? ['--testPathPatterns', options.testPathPatterns] : []),
74
82
  ...(options.testNamePattern ? ['--testNamePattern', options.testNamePattern] : []),
75
- ...(options.maxWorkers ? ['--maxWorkers', options.maxWorkers] : []),
76
- ...(options.u || options.updateSnapshot ? ['--updateSnapshot'] : ['']),
83
+ ...(options.maxWorkers ? ['--maxWorkers', `${options.maxWorkers}`] : []),
84
+ ...(options.u || options.updateSnapshot ? ['--updateSnapshot'] : []),
77
85
  // Only include the positional args if `options._` wasn't specified
78
86
  // (to avoid possibly including them twice)
79
87
  ...(options._ || positional),
80
88
  ].filter(arg => !!arg) as string[];
81
89
 
82
- logger.info(cmd, encodeArgs(args).join(' '));
90
+ logNodeCommand(args);
83
91
 
84
- return spawn(cmd, args, { stdio: 'inherit', env: options.env });
85
- } else {
86
- logger.warn('no jest configuration found, skipping jest');
87
- return Promise.resolve();
92
+ return spawn(process.execPath, args, { stdio: 'inherit', env: options.env });
88
93
  }
94
+
95
+ logger.warn('no jest configuration found, skipping jest');
96
+ // undertaker apparently requires returning a promise, async function, or function that calls done()
97
+ return Promise.resolve();
89
98
  };
90
99
  }
@@ -6,37 +6,51 @@ import { getTsNodeEnv } from '../typescript/getTsNodeEnv';
6
6
 
7
7
  export interface NodeExecTaskOptions {
8
8
  /**
9
- * Arguments to be passed into a spawn call for webpack dev server. This can be used to do things
10
- * like increase the heap space for the JS engine to address out of memory issues.
9
+ * Arguments to be passed into a spawn call, including the script path to execute.
10
+ * The script path should be **absolute** to prevent unpredictable resolution.
11
+ *
12
+ * **WARNING: If `options.shell` is enabled, do not pass unsanitized user input as `args`.
13
+ * Any input containing shell metacharacters may be used to trigger arbitrary command execution.**
11
14
  */
12
- args?: string[];
15
+ args: string[];
13
16
 
14
17
  /**
15
- * Environment variables to be passed to the webpack-cli
18
+ * Environment variables to be passed to the spawned process.
19
+ * Defaults to `process.env`.
16
20
  */
17
21
  env?: NodeJS.ProcessEnv;
18
22
 
19
23
  /**
20
- * Should this nodeExec task be using something like ts-node to execute the binary
24
+ * Whether to use `ts-node` to execute the script
21
25
  */
22
26
  enableTypeScript?: boolean;
23
27
 
24
28
  /**
25
- * The tsconfig file to pass to ts-node for Typescript config
29
+ * tsconfig file path to pass to `ts-node`
26
30
  */
27
31
  tsconfig?: string;
28
32
 
29
33
  /**
30
- * Transpile the config only
34
+ * Whether to use `transpileOnly` mode for `ts-node`
31
35
  */
32
36
  transpileOnly?: boolean;
33
37
 
34
38
  /**
35
- * Custom spawn options
39
+ * Custom spawn options.
40
+ *
41
+ * **WARNING: If the `shell` option is enabled, do not pass unsanitized user input as `args`.
42
+ * Any input containing shell metacharacters may be used to trigger arbitrary command execution.**
36
43
  */
37
44
  spawnOptions?: SpawnOptions;
38
45
  }
39
46
 
47
+ /**
48
+ * Create a task to execute a command in a new process.
49
+ * Uses `cross-spawn` to avoid issues with spaces in arguments, but does not do any additional escaping.
50
+ *
51
+ * **WARNING: If the `shell` option is enabled, do not pass unsanitized user input to this task.
52
+ * Any input containing shell metacharacters may be used to trigger arbitrary command execution.**
53
+ */
40
54
  export function nodeExecTask(options: NodeExecTaskOptions): TaskFunction {
41
55
  return function () {
42
56
  const { spawnOptions, enableTypeScript, tsconfig, transpileOnly } = options;
@@ -44,17 +58,19 @@ export function nodeExecTask(options: NodeExecTaskOptions): TaskFunction {
44
58
  const tsNodeRegister = resolveCwd('ts-node/register');
45
59
  const nodeExecPath = process.execPath;
46
60
 
47
- if (enableTypeScript && tsNodeRegister) {
48
- options.args = options.args || [];
49
- options.args.unshift(tsNodeRegister);
50
- options.args.unshift('-r');
61
+ const args = [...(options.args || [])];
62
+ // Preserve the default behavior of inheriting process.env if no options are specified
63
+ let env = options.env ? { ...options.env } : { ...process.env };
64
+ const isTS = enableTypeScript && tsNodeRegister;
51
65
 
52
- options.env = { ...options.env, ...getTsNodeEnv(tsconfig, transpileOnly) };
53
- logger.info('Executing [TS]: ' + [nodeExecPath, ...(options.args || [])].join(' '));
54
- } else {
55
- logger.info('Executing: ' + [nodeExecPath, ...(options.args || [])].join(' '));
66
+ if (isTS) {
67
+ args.unshift('-r', tsNodeRegister);
68
+
69
+ env = { ...env, ...getTsNodeEnv(tsconfig, transpileOnly) };
56
70
  }
57
71
 
58
- return spawn(nodeExecPath, options.args, { stdio: 'inherit', env: options.env, ...spawnOptions });
72
+ logger.info([`Executing${isTS ? ' [TS]' : ''}:`, nodeExecPath, ...args].join(' '));
73
+
74
+ return spawn(nodeExecPath, args, { stdio: 'inherit', env, ...spawnOptions });
59
75
  };
60
76
  }
@@ -1,5 +1,5 @@
1
1
  import { logger, resolve, TaskFunction } from 'just-task';
2
- import { spawn } from '../utils';
2
+ import { logNodeCommand, spawn } from '../utils';
3
3
  import { splitArrayIntoChunks } from '../arrayUtils/splitArrayIntoChunks';
4
4
  import * as path from 'path';
5
5
  import { arrayify } from '../arrayUtils/arrayify';
@@ -16,10 +16,12 @@ export interface PrettierTaskOptions {
16
16
  files?: string[] | string;
17
17
  ignorePath?: string;
18
18
  configPath?: string;
19
+ check?: boolean;
19
20
  }
20
21
 
21
22
  export function prettierTask(options: PrettierTaskOptions = {}): TaskFunction {
22
- const prettierBin = resolve('prettier/bin-prettier.js');
23
+ // check v2 or v3 path
24
+ const prettierBin = resolve('prettier/bin-prettier.js') || resolve('prettier/bin/prettier.cjs');
23
25
 
24
26
  if (prettierBin) {
25
27
  return function prettier() {
@@ -32,38 +34,19 @@ export function prettierTask(options: PrettierTaskOptions = {}): TaskFunction {
32
34
  options.files || path.resolve(process.cwd(), '**', '*.{ts,tsx,js,jsx,json,scss,html,yml,md}'),
33
35
  ),
34
36
  },
35
- check: false,
37
+ check: !!options.check,
36
38
  });
37
39
  };
38
40
  }
39
41
 
40
- return function () {
42
+ // undertaker apparently requires returning a promise, async function, or function that calls done()
43
+ return async () => {
41
44
  logger.warn('Prettier is not available, ignoring this task');
42
45
  };
43
46
  }
44
47
 
45
48
  export function prettierCheckTask(options: PrettierTaskOptions = {}): TaskFunction {
46
- const prettierBin = resolve('prettier/bin-prettier.js');
47
-
48
- if (prettierBin) {
49
- return function prettierCheck() {
50
- return runPrettierAsync({
51
- prettierBin,
52
- ...{ configPath: options.configPath || undefined },
53
- ...{ ignorePath: options.ignorePath || undefined },
54
- ...{
55
- files: arrayify(
56
- options.files || path.resolve(process.cwd(), '**', '*.{ts,tsx,js,jsx,json,scss,html,yml,md}'),
57
- ),
58
- },
59
- check: true,
60
- });
61
- };
62
- }
63
-
64
- return function () {
65
- logger.warn('Prettier is not available, ignoring this task');
66
- };
49
+ return prettierTask({ ...options, check: true });
67
50
  }
68
51
 
69
52
  function runPrettierAsync(context: PrettierContext) {
@@ -81,7 +64,7 @@ function runPrettierAsync(context: PrettierContext) {
81
64
  ...chunk,
82
65
  ];
83
66
 
84
- logger.info(process.execPath + ' ' + prettierArgs.join(' '));
67
+ logNodeCommand(prettierArgs);
85
68
 
86
69
  return finishPromise.then(() => spawn(process.execPath, prettierArgs, { stdio: 'inherit' }));
87
70
  }, Promise.resolve());
@@ -1,6 +1,6 @@
1
1
  import * as ts from 'typescript';
2
2
  import { resolve, logger, resolveCwd, TaskFunction } from 'just-task';
3
- import { exec, encodeArgs, spawn } from '../utils';
3
+ import { logNodeCommand, spawn } from '../utils';
4
4
  import * as fs from 'fs';
5
5
 
6
6
  export type TscTaskOptions = { [key in keyof ts.CompilerOptions]?: string | boolean | string[] } & {
@@ -25,9 +25,8 @@ export function tscTask(options: TscTaskOptions = {}): TaskFunction {
25
25
  logger.info(`Running ${tscCmd} with ${options.project || options.build}`);
26
26
 
27
27
  const args = argsFromOptions(tscCmd, options);
28
- const cmd = encodeArgs([process.execPath, ...args]).join(' ');
29
- logger.info(`Executing: ${cmd}`);
30
- return exec(cmd);
28
+ logNodeCommand(args);
29
+ return spawn(process.execPath, args, { stdio: 'inherit' });
31
30
  }
32
31
  return Promise.resolve();
33
32
  };
@@ -37,25 +36,7 @@ export function tscTask(options: TscTaskOptions = {}): TaskFunction {
37
36
  * Returns a task that runs the TSC CLI in watch mode.
38
37
  */
39
38
  export function tscWatchTask(options: TscTaskOptions = {}): TaskFunction {
40
- const tscCmd = resolve('typescript/lib/tsc.js');
41
-
42
- if (!tscCmd) {
43
- throw new Error('cannot find tsc');
44
- }
45
-
46
- return function tscWatch() {
47
- options = { ...options, ...getProjectOrBuildOptions(options) };
48
-
49
- if (isValidProject(options)) {
50
- logger.info(`Running ${tscCmd} with ${options.project || options.build} in watch mode`);
51
-
52
- const args = argsFromOptions(tscCmd, options);
53
- const cmd = [...args, '--watch'];
54
- logger.info(encodeArgs(cmd).join(' '));
55
- return spawn(process.execPath, cmd, { stdio: 'inherit' });
56
- }
57
- return Promise.resolve();
58
- };
39
+ return tscTask({ ...options, watch: true });
59
40
  }
60
41
 
61
42
  /**
@@ -81,9 +62,7 @@ function isValidProject(options: TscTaskOptions) {
81
62
  (typeof options.project === 'string' && fs.existsSync(options.project)) ||
82
63
  (typeof options.build === 'string' && fs.existsSync(options.build)) ||
83
64
  (Array.isArray(options.build) &&
84
- options.build.reduce((currentIsValid, buildPath) => {
85
- return currentIsValid && typeof buildPath === 'string' && fs.existsSync(buildPath);
86
- }, true as boolean))
65
+ options.build.every(buildPath => typeof buildPath === 'string' && fs.existsSync(buildPath)))
87
66
  );
88
67
  }
89
68
 
@@ -91,23 +70,21 @@ function isValidProject(options: TscTaskOptions) {
91
70
  * Returns an array of CLI arguments for TSC given the `options`.
92
71
  */
93
72
  function argsFromOptions(tscCmd: string, options: TscTaskOptions): string[] {
94
- const { nodeArgs, ...rest } = options;
73
+ const { nodeArgs, build, ...rest } = options;
74
+
75
+ const args = [...(nodeArgs || []), tscCmd];
76
+ // --build must be the first arg if specified
77
+ const argEntries = [...(build !== undefined ? [['build', build]] : []), ...Object.entries(rest)];
78
+
79
+ for (const [option, optionValue] of argEntries) {
80
+ if (typeof optionValue === 'string') {
81
+ args.push('--' + option, optionValue);
82
+ } else if (typeof optionValue === 'boolean' && optionValue) {
83
+ args.push('--' + option);
84
+ } else if (Array.isArray(optionValue)) {
85
+ args.push('--' + option, ...optionValue);
86
+ }
87
+ }
95
88
 
96
- return [
97
- ...(nodeArgs ? nodeArgs : []),
98
- ...Object.keys(rest).reduce(
99
- (currentArgs, option) => {
100
- const optionValue = options[option];
101
- if (typeof optionValue === 'string') {
102
- return currentArgs.concat(['--' + option, optionValue]);
103
- } else if (typeof optionValue === 'boolean' && optionValue) {
104
- return currentArgs.concat(['--' + option]);
105
- } else if (Array.isArray(optionValue)) {
106
- return currentArgs.concat(['--' + option, ...optionValue]);
107
- }
108
- return currentArgs;
109
- },
110
- [tscCmd],
111
- ),
112
- ];
89
+ return args;
113
90
  }