@fynjs/run 1.0.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.
- package/LICENSE +201 -0
- package/README.md +720 -0
- package/bin/qrun.js +3 -0
- package/bin/xrun.js +42 -0
- package/cli/check-global.js +6 -0
- package/cli/ck.js +6 -0
- package/cli/cli-options.js +98 -0
- package/cli/config.js +8 -0
- package/cli/env.js +33 -0
- package/cli/npm-loader.js +56 -0
- package/cli/parse-cmd-args.js +156 -0
- package/cli/provider-packages.js +73 -0
- package/cli/search-up-task-file.js +92 -0
- package/cli/task-file.js +192 -0
- package/cli/ts-runner.js +51 -0
- package/cli/usage.js +6 -0
- package/cli/wrap-process.js +27 -0
- package/cli/xrun-main.js +425 -0
- package/cli/xrun.js +8 -0
- package/lib/chalk.js +11 -0
- package/lib/cli-context.js +145 -0
- package/lib/defaults.js +32 -0
- package/lib/gen-xqid.js +10 -0
- package/lib/index.js +12 -0
- package/lib/logger.js +105 -0
- package/lib/ns-order.js +76 -0
- package/lib/print-tasks/index.js +105 -0
- package/lib/reporters/console.js +133 -0
- package/lib/stringify.js +16 -0
- package/lib/util/parse-array.js +5 -0
- package/lib/util/update-env.js +16 -0
- package/lib/xqitem.js +70 -0
- package/lib/xqtor.js +814 -0
- package/lib/xqtree.js +41 -0
- package/lib/xrun-instance.js +33 -0
- package/lib/xrun.d.ts +89 -0
- package/lib/xrun.js +344 -0
- package/lib/xtask-spec.js +63 -0
- package/lib/xtasks.js +137 -0
- package/package.json +93 -0
package/bin/qrun.js
ADDED
package/bin/xrun.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import Path from "path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Find the directory holding the CLI code to run.
|
|
10
|
+
*
|
|
11
|
+
* A project can install its own @fynjs/run, and that copy should win over whichever one this bin
|
|
12
|
+
* happens to live in - so look from the project's cwd first, then from this file. Looking from
|
|
13
|
+
* this file also covers package managers that symlink node_modules/.bin straight at the bin
|
|
14
|
+
* script, which bypasses node's `--preserve-symlinks` handling.
|
|
15
|
+
*
|
|
16
|
+
* Every branch returns an absolute path. Probing by name and then requiring by name would use two
|
|
17
|
+
* different resolution bases - cwd for the probe, this file for the require - so they could land
|
|
18
|
+
* on different copies, or the require could fail outright when only the probe matched.
|
|
19
|
+
*
|
|
20
|
+
* @returns absolute path to the cli directory
|
|
21
|
+
*/
|
|
22
|
+
function resolveCliDir() {
|
|
23
|
+
for (const paths of [[process.cwd()], [import.meta.dirname]]) {
|
|
24
|
+
try {
|
|
25
|
+
return Path.dirname(require.resolve("@fynjs/run/cli/xrun", { paths }));
|
|
26
|
+
} catch (err) {
|
|
27
|
+
//
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// not installed under either - running from this package's own checkout
|
|
32
|
+
return Path.join(import.meta.dirname, "..", "cli");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//
|
|
36
|
+
// resolve xrun and ck from the same install so they cannot come from different copies
|
|
37
|
+
//
|
|
38
|
+
const cliDir = resolveCliDir();
|
|
39
|
+
|
|
40
|
+
const { default: xrun } = await import(pathToFileURL(Path.join(cliDir, "xrun.js")).href);
|
|
41
|
+
|
|
42
|
+
await xrun();
|
package/cli/ck.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//
|
|
2
|
+
// chalker is the tagged-template color formatter used across the CLI. It is ESM with a
|
|
3
|
+
// top-level await, which a CJS package could not require - hence the async loader and
|
|
4
|
+
// marker-stripping fallback this module used to carry. As ESM we simply import it.
|
|
5
|
+
//
|
|
6
|
+
export { default } from "chalker";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import chalk from "../lib/chalk.js";
|
|
3
|
+
import myPkg from "../package.json" with { type: "json" };
|
|
4
|
+
import config from "./config.js";
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
cwd: {
|
|
8
|
+
alias: "w",
|
|
9
|
+
desc: `Set ${myPkg.name}'s ${chalk.magenta("CWD")}`,
|
|
10
|
+
args: "<path string>",
|
|
11
|
+
requireArg: true
|
|
12
|
+
},
|
|
13
|
+
dir: {
|
|
14
|
+
alias: "d",
|
|
15
|
+
desc: `Set dir to look for ${chalk.green(config.taskFile)} (default is ${chalk.magenta(
|
|
16
|
+
"CWD"
|
|
17
|
+
)})`,
|
|
18
|
+
args: "<path string>",
|
|
19
|
+
requireArg: true
|
|
20
|
+
},
|
|
21
|
+
npm: {
|
|
22
|
+
alias: "n",
|
|
23
|
+
desc: `load npm scripts into namespace ${chalk.magenta("npm")} (--no-npm to disable)`,
|
|
24
|
+
args: "[ boolean]",
|
|
25
|
+
argDefault: "true"
|
|
26
|
+
},
|
|
27
|
+
nmbin: {
|
|
28
|
+
alias: "b",
|
|
29
|
+
desc: `add ${chalk.magenta("CWD/node_modules/.bin")} to ${chalk.blue("PATH")}`,
|
|
30
|
+
args: "[ boolean]",
|
|
31
|
+
argDefault: "true"
|
|
32
|
+
},
|
|
33
|
+
list: {
|
|
34
|
+
alias: "l",
|
|
35
|
+
desc: "List tasks names from list of comma separated namespaces (default is all namespaces)",
|
|
36
|
+
args: "[namespaces string]",
|
|
37
|
+
argDefault: ""
|
|
38
|
+
},
|
|
39
|
+
full: {
|
|
40
|
+
type: "count",
|
|
41
|
+
alias: "f",
|
|
42
|
+
desc: "--list show tasks names with namespace",
|
|
43
|
+
counting: Infinity
|
|
44
|
+
},
|
|
45
|
+
ns: {
|
|
46
|
+
alias: "m",
|
|
47
|
+
desc: "List all namespaces",
|
|
48
|
+
args: "[ boolean]"
|
|
49
|
+
},
|
|
50
|
+
soe: {
|
|
51
|
+
alias: "e",
|
|
52
|
+
desc: `Stop on errors - one of: no, soft, full`,
|
|
53
|
+
args: "[mode enum]",
|
|
54
|
+
argDefault: "full",
|
|
55
|
+
customTypes: {
|
|
56
|
+
enum: v => {
|
|
57
|
+
/* istanbul ignore next */
|
|
58
|
+
if (v === undefined) {
|
|
59
|
+
return "full";
|
|
60
|
+
}
|
|
61
|
+
/* istanbul ignore next */
|
|
62
|
+
if (!v || v === "no") {
|
|
63
|
+
/* istanbul ignore next */
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
assert(v === "soft" || v === "full", `option soe value must be one of: no, soft, full`);
|
|
67
|
+
return v;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
quiet: {
|
|
72
|
+
alias: "q",
|
|
73
|
+
desc: "Do not output any logs",
|
|
74
|
+
args: "[ boolean]",
|
|
75
|
+
argDefault: "false"
|
|
76
|
+
},
|
|
77
|
+
serial: {
|
|
78
|
+
alias: ["s", "x", "ser"],
|
|
79
|
+
desc: "Execute tasks from command line serially",
|
|
80
|
+
args: "[ boolean]",
|
|
81
|
+
argDefault: "false"
|
|
82
|
+
},
|
|
83
|
+
concurrent: {
|
|
84
|
+
alias: ["c", "conc"],
|
|
85
|
+
desc: "Execute tasks from command line concurrently (default)",
|
|
86
|
+
args: "[ boolean]",
|
|
87
|
+
argDefault: "true"
|
|
88
|
+
},
|
|
89
|
+
env: {
|
|
90
|
+
desc: "Set env. ie: --env NODE_ENV=development FOO=BAR -.",
|
|
91
|
+
args: "[env string...]"
|
|
92
|
+
},
|
|
93
|
+
require: {
|
|
94
|
+
alias: "r",
|
|
95
|
+
desc: `require module for tasks instead of loading ${config.taskFile}. require from path is CWD`,
|
|
96
|
+
args: "[modules string...]"
|
|
97
|
+
}
|
|
98
|
+
};
|
package/cli/config.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import myPkg from "../package.json" with { type: "json" };
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
taskFile: "xrun-tasks.js",
|
|
5
|
+
taskFileExt: ["js", "cjs", "ts", "mts", "cts", "mjs"],
|
|
6
|
+
search: ["xrun-tasks", "xrun", "xclap.", "clapfile.", "clap.", "gulpfile."],
|
|
7
|
+
getPkgOpt: pkg => ["xclap", "xrun", myPkg.name].find(f => pkg.hasOwnProperty(f))
|
|
8
|
+
};
|
package/cli/env.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import WrapProcess from "./wrap-process.js";
|
|
3
|
+
|
|
4
|
+
const lib = {
|
|
5
|
+
container: WrapProcess.env,
|
|
6
|
+
xrunTaskFile: "XRUN_TASKFILE",
|
|
7
|
+
xrunPackagePath: "XRUN_PACKAGE_PATH",
|
|
8
|
+
xrunId: "XRUN_ID",
|
|
9
|
+
forceColor: "FORCE_COLOR",
|
|
10
|
+
xrunCwd: "XRUN_CWD",
|
|
11
|
+
xrunVersion: "XRUN_VERSION",
|
|
12
|
+
xrunBinDir: "XRUN_BIN_DIR",
|
|
13
|
+
xrunNodeBin: "XRUN_NODE_BIN",
|
|
14
|
+
xrunQuiet: "XRUN_QUIET",
|
|
15
|
+
get(key) {
|
|
16
|
+
assert(key, `env.get invalid key: ${key}`);
|
|
17
|
+
return lib.container[key];
|
|
18
|
+
},
|
|
19
|
+
set(key, val) {
|
|
20
|
+
assert(key, `env.set invalid key: ${key}`);
|
|
21
|
+
lib.container[key] = `${val}`;
|
|
22
|
+
},
|
|
23
|
+
has(key) {
|
|
24
|
+
assert(key, `env.has invalid key: ${key}`);
|
|
25
|
+
return lib.container.hasOwnProperty(key);
|
|
26
|
+
},
|
|
27
|
+
del(key) {
|
|
28
|
+
assert(key, `env.del invalid key: ${key}`);
|
|
29
|
+
delete lib.container[key];
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default lib;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import logger from "../lib/logger.js";
|
|
2
|
+
import chalk from "../lib/chalk.js";
|
|
3
|
+
import readPkgUp from "read-pkg-up";
|
|
4
|
+
import myPkg from "../package.json" with { type: "json" };
|
|
5
|
+
import config from "./config.js";
|
|
6
|
+
import env from "./env.js";
|
|
7
|
+
|
|
8
|
+
export default (xrun, options) => {
|
|
9
|
+
const readPkg = readPkgUp.sync();
|
|
10
|
+
|
|
11
|
+
if (!readPkg) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Pkg = readPkg.packageJson;
|
|
16
|
+
|
|
17
|
+
const pkgName = chalk.magenta(readPkg.path.replace(process.cwd(), "."));
|
|
18
|
+
|
|
19
|
+
if (Pkg.scripts && options.npm !== false) {
|
|
20
|
+
const scripts = {};
|
|
21
|
+
for (const k in Pkg.scripts) {
|
|
22
|
+
if (!k.startsWith("pre") && !k.startsWith("post")) {
|
|
23
|
+
const pre = `pre${k}`;
|
|
24
|
+
const post = `post${k}`;
|
|
25
|
+
scripts[k] = xrun.serial(
|
|
26
|
+
Pkg.scripts.hasOwnProperty(pre) && pre,
|
|
27
|
+
xrun.exec(Pkg.scripts[k], "npm"),
|
|
28
|
+
Pkg.scripts.hasOwnProperty(post) && post
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
scripts[k] = xrun.exec(Pkg.scripts[k], "npm");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
xrun.load("npm", scripts);
|
|
35
|
+
/* istanbul ignore else */
|
|
36
|
+
if (env.get(env.xrunPackagePath) !== readPkg.path) {
|
|
37
|
+
logger.log(`Loaded npm scripts from ${pkgName} into namespace ${chalk.magenta("npm")}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
env.set(env.xrunPackagePath, readPkg.path);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pkgOptField = config.getPkgOpt(Pkg);
|
|
44
|
+
const pkgConfig = pkgOptField && Pkg[pkgOptField];
|
|
45
|
+
|
|
46
|
+
if (pkgConfig) {
|
|
47
|
+
const tasks = Object.assign({}, pkgConfig.tasks);
|
|
48
|
+
/* istanbul ignore else */
|
|
49
|
+
if (Object.keys(tasks).length > 0) {
|
|
50
|
+
xrun.load("pkg", tasks);
|
|
51
|
+
logger.log(
|
|
52
|
+
`Loaded ${myPkg.name} tasks from ${pkgName} into namespace ${chalk.magenta("pkg")}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} SearchResult
|
|
3
|
+
* @property {boolean} found - Whether a task file was found
|
|
4
|
+
* @property {boolean} foundPkg - Whether a package.json was found
|
|
5
|
+
* @property {string} [xrunFile] - Path to the found task file
|
|
6
|
+
* @property {string} dir - Directory where the file was found
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} ParseOptions
|
|
11
|
+
* @property {string} [cwd] - Current working directory
|
|
12
|
+
* @property {string} [dir] - Directory to search in
|
|
13
|
+
* @property {string[]} [require] - Modules to require
|
|
14
|
+
* @property {boolean} [npm] - Whether to load npm tasks
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} ParseResult
|
|
19
|
+
* @property {string[]} tasks - List of tasks to run
|
|
20
|
+
* @property {Object} parsed - Parsed command line arguments
|
|
21
|
+
* @property {SearchResult} searchResult - Result of searching for task files
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import Path from "path";
|
|
25
|
+
import cliOptions from "./cli-options.js";
|
|
26
|
+
import { NixClap } from "@fynjs/cli-args";
|
|
27
|
+
import xsh from "xsh";
|
|
28
|
+
import usage from "./usage.js";
|
|
29
|
+
import logger from "../lib/logger.js";
|
|
30
|
+
import myPkg from "../package.json" with { type: "json" };
|
|
31
|
+
import ck from "./ck.js";
|
|
32
|
+
import config from "./config.js";
|
|
33
|
+
import env from "./env.js";
|
|
34
|
+
import fs from "fs";
|
|
35
|
+
import { updateCwd, searchTaskFile, loadTaskFile, processTasks, loadTasks } from "./task-file.js";
|
|
36
|
+
import { loadProviderPackages } from "./provider-packages.js";
|
|
37
|
+
import WrapProcess from "./wrap-process.js";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read and parse package.json from a directory
|
|
41
|
+
* @param {string} dir - Directory containing package.json
|
|
42
|
+
* @returns {Object} Parsed package.json contents
|
|
43
|
+
*/
|
|
44
|
+
function readPackageJson(dir) {
|
|
45
|
+
try {
|
|
46
|
+
const pkgData = fs.readFileSync(Path.join(dir, "package.json"), "utf-8");
|
|
47
|
+
return JSON.parse(pkgData);
|
|
48
|
+
} catch {
|
|
49
|
+
/* istanbul ignore next */
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parse command line arguments
|
|
56
|
+
* @param {string[]} argv - Command line arguments
|
|
57
|
+
* @param {number} start - Index to start parsing from
|
|
58
|
+
* @returns {ParseResult} Parsed arguments and tasks
|
|
59
|
+
*/
|
|
60
|
+
async function parseArgs(argv, start) {
|
|
61
|
+
const nc = new NixClap({
|
|
62
|
+
allowUnknownCommand: true, // Allow task names as commands
|
|
63
|
+
allowUnknownOption: true, // Allow task-specific options
|
|
64
|
+
handlers: {
|
|
65
|
+
/* istanbul ignore next */
|
|
66
|
+
exit: code => {
|
|
67
|
+
WrapProcess.exit(code);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
.version(myPkg.version)
|
|
72
|
+
.usage(usage)
|
|
73
|
+
.init(
|
|
74
|
+
cliOptions,
|
|
75
|
+
{} // no xrun commands
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Parse all arguments at once - @fynjs/cli-args handles command/option separation
|
|
79
|
+
const parsed = nc.parse(argv, start);
|
|
80
|
+
const opts = parsed.command.opts;
|
|
81
|
+
|
|
82
|
+
const myDir = xsh.pathCwd.replace(Path.dirname(import.meta.dirname), ".");
|
|
83
|
+
|
|
84
|
+
/* istanbul ignore next */
|
|
85
|
+
if (env.get(env.xrunVersion) !== myPkg.version || env.get(env.xrunBinDir) !== myDir) {
|
|
86
|
+
logger.log(ck`<green>${myPkg.name}</> version ${myPkg.version} at <magenta>${myDir}</>`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* istanbul ignore next */
|
|
90
|
+
if (env.get(env.xrunNodeBin) !== process.execPath) {
|
|
91
|
+
logger.log(ck`<green>node.js</> version ${process.version} at <magenta>${process.execPath}</>`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
env.set(env.xrunVersion, myPkg.version);
|
|
95
|
+
env.set(env.xrunBinDir, myDir);
|
|
96
|
+
env.set(env.xrunNodeBin, process.execPath);
|
|
97
|
+
|
|
98
|
+
// don't search if user has explicitly set CWD
|
|
99
|
+
const search = !opts.cwd;
|
|
100
|
+
|
|
101
|
+
const saveCwd = env.get(env.xrunCwd);
|
|
102
|
+
opts.cwd = updateCwd(opts.cwd);
|
|
103
|
+
|
|
104
|
+
let searchResult = {};
|
|
105
|
+
|
|
106
|
+
/* istanbul ignore next */
|
|
107
|
+
if (!opts.require) {
|
|
108
|
+
searchResult = searchTaskFile(search, opts);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const Pkg = readPackageJson(opts.cwd);
|
|
112
|
+
|
|
113
|
+
const pkgOptField = config.getPkgOpt(Pkg);
|
|
114
|
+
let pkgConfig = {};
|
|
115
|
+
|
|
116
|
+
if (pkgOptField) {
|
|
117
|
+
pkgConfig = Object.assign(pkgConfig, Pkg[pkgOptField]);
|
|
118
|
+
delete pkgConfig.cwd; // not allow pkg config to override cwd
|
|
119
|
+
delete pkgConfig.tasks;
|
|
120
|
+
// nc.applyConfig(pkgConfig, parsed);
|
|
121
|
+
const pkgName = ck`<magenta>./package.json</>`;
|
|
122
|
+
logger.log(ck`Applied <green>${pkgOptField}</> options from ${pkgName}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const loaded = await loadTasks(opts, searchResult);
|
|
126
|
+
|
|
127
|
+
// Extract tasks from commands
|
|
128
|
+
const tasks = Object.keys(parsed.command.subCmdNodes);
|
|
129
|
+
|
|
130
|
+
// user has no tasks or explicitly enable searching for provider modules
|
|
131
|
+
if (loaded === false || pkgConfig.loadProviderModules) {
|
|
132
|
+
loadProviderPackages(Pkg, saveCwd, opts);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
opts,
|
|
137
|
+
tasks,
|
|
138
|
+
parsed,
|
|
139
|
+
searchResult
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
parseArgs,
|
|
145
|
+
updateCwd,
|
|
146
|
+
searchTaskFile,
|
|
147
|
+
loadTaskFile,
|
|
148
|
+
processTasks
|
|
149
|
+
};
|
|
150
|
+
export default {
|
|
151
|
+
parseArgs,
|
|
152
|
+
updateCwd,
|
|
153
|
+
searchTaskFile,
|
|
154
|
+
loadTaskFile,
|
|
155
|
+
processTasks
|
|
156
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { makeOptionalRequire } from "optional-require";
|
|
2
|
+
import { processTasks } from "./task-file.js";
|
|
3
|
+
import WrapProcess from "./wrap-process.js";
|
|
4
|
+
import myPkg from "../package.json" with { type: "json" };
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Search for provider packages in the node_modules directory.
|
|
9
|
+
*
|
|
10
|
+
* When does xrun load provider packages?
|
|
11
|
+
*
|
|
12
|
+
* 1. When the user has no tasks loaded
|
|
13
|
+
* 2. When the user sets the `loadProviderModules` option in their package.json under the `xrun` key
|
|
14
|
+
*
|
|
15
|
+
* What's considered a provider package?
|
|
16
|
+
*
|
|
17
|
+
* 1. The package sets the `xrunProvider` config in its package.json
|
|
18
|
+
* 2. The package has @fynjs/run listed in its dependencies
|
|
19
|
+
*
|
|
20
|
+
* What module does xrun require from the provider package to look for tasks?
|
|
21
|
+
*
|
|
22
|
+
* 1. The provider package sets `module` prop under the `xrunProvider` config in its package.json
|
|
23
|
+
* 2. Otherwise, xrun will require the provider package using its name
|
|
24
|
+
*
|
|
25
|
+
* How does xrun load tasks from the provider package?
|
|
26
|
+
*
|
|
27
|
+
* 1. If the provider module has a `loadTasks` export function, it will be used to load tasks
|
|
28
|
+
*
|
|
29
|
+
* @param {*} userPkg - the user's package.json object, typically the one in CWD
|
|
30
|
+
* @param {*} saveCwd - the CWD when xrun started, before xrun updated it to user specified CWD
|
|
31
|
+
* @param {*} opts - the command line options
|
|
32
|
+
*/
|
|
33
|
+
function loadProviderPackages(userPkg, saveCwd, opts) {
|
|
34
|
+
const optionalRequire = makeOptionalRequire(opts.cwd || WrapProcess.cwd());
|
|
35
|
+
const providers = Object.keys(
|
|
36
|
+
Object.assign({}, userPkg.optionalDependencies, userPkg.devDependencies, userPkg.dependencies)
|
|
37
|
+
);
|
|
38
|
+
providers.forEach(pkgName => {
|
|
39
|
+
const providerPath = optionalRequire.resolvePath(pkgName);
|
|
40
|
+
const pkgJsonPath =
|
|
41
|
+
providerPath &&
|
|
42
|
+
path.join(
|
|
43
|
+
providerPath.substring(0, pkgName.length + providerPath.indexOf(pkgName)),
|
|
44
|
+
`package.json`
|
|
45
|
+
);
|
|
46
|
+
const providerPkg = pkgJsonPath && optionalRequire(pkgJsonPath);
|
|
47
|
+
if (!providerPkg) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const provider = providerPkg.xrunProvider;
|
|
51
|
+
// accept both the current name (@fynjs/run) and the legacy name (@xarc/run) so
|
|
52
|
+
// providers published before the rename are still detected
|
|
53
|
+
const XRUN_DEP_NAMES = [myPkg.name, "@xarc/run"];
|
|
54
|
+
if (!provider && !XRUN_DEP_NAMES.some(n => providerPkg.dependencies?.[n])) {
|
|
55
|
+
// module is not marked as a provider and doesn't have xrun as dep, assume not
|
|
56
|
+
// a provider
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// module looks like a provider and user does not have tasks loaded, continue
|
|
60
|
+
// to see if module exports `loadTasks`
|
|
61
|
+
|
|
62
|
+
const req = (provider?.module && `/${provider.module}`) || "";
|
|
63
|
+
const providerMod = optionalRequire(`${pkgName}${req}`);
|
|
64
|
+
if (providerMod?.loadTasks) {
|
|
65
|
+
const loadMsg = saveCwd !== opts.cwd ? `provider package ${pkgName}` : "";
|
|
66
|
+
processTasks(providerMod.loadTasks, loadMsg);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
loadProviderPackages
|
|
73
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Path from "path";
|
|
2
|
+
import Fs from "fs";
|
|
3
|
+
import config from "./config.js";
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Look for xrun file at xrunDir
|
|
7
|
+
* Search up each directory if `search` is true
|
|
8
|
+
* Until a file "package.json" is found or top is reached
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
function searchUpTaskFile(xrunDir, search) {
|
|
12
|
+
let result;
|
|
13
|
+
let dir = xrunDir;
|
|
14
|
+
|
|
15
|
+
do {
|
|
16
|
+
result = findTaskFile(dir);
|
|
17
|
+
|
|
18
|
+
// If we found a task file or package.json, stop searching
|
|
19
|
+
if (result.found || result.foundPkg) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// If we're not searching up or we've hit the root, stop
|
|
24
|
+
if (!search) break;
|
|
25
|
+
|
|
26
|
+
const tmp = Path.join(dir, "..");
|
|
27
|
+
if (!tmp || tmp === "." || tmp === dir) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
dir = tmp;
|
|
31
|
+
} while (true);
|
|
32
|
+
|
|
33
|
+
// If we didn't find a task file, use the original directory
|
|
34
|
+
if (!result.found) {
|
|
35
|
+
result.dir = xrunDir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function findTaskFile(xrunDir) {
|
|
42
|
+
const dirFiles = Fs.readdirSync(xrunDir);
|
|
43
|
+
const files = config.search;
|
|
44
|
+
|
|
45
|
+
// First check the root directory
|
|
46
|
+
let xrunFile;
|
|
47
|
+
files.find(n => (xrunFile = dirFiles.find(f => f.startsWith(n))));
|
|
48
|
+
|
|
49
|
+
// If found in root, return it
|
|
50
|
+
if (xrunFile) {
|
|
51
|
+
const foundPkg = Boolean(dirFiles.find(f => f === "package.json"));
|
|
52
|
+
return {
|
|
53
|
+
found: true,
|
|
54
|
+
foundPkg,
|
|
55
|
+
xrunFile: Path.join(xrunDir, xrunFile),
|
|
56
|
+
dir: xrunDir
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// If not found in root, check subdirectories: scripts/, tools/, build/, tasks/
|
|
61
|
+
const subDirs = ["scripts", "tools", "build", "tasks"];
|
|
62
|
+
for (const subDir of subDirs) {
|
|
63
|
+
const subDirPath = Path.join(xrunDir, subDir);
|
|
64
|
+
try {
|
|
65
|
+
const subDirFiles = Fs.readdirSync(subDirPath);
|
|
66
|
+
files.find(n => (xrunFile = subDirFiles.find(f => f.startsWith(n))));
|
|
67
|
+
|
|
68
|
+
if (xrunFile) {
|
|
69
|
+
const foundPkg = Boolean(dirFiles.find(f => f === "package.json"));
|
|
70
|
+
return {
|
|
71
|
+
found: true,
|
|
72
|
+
foundPkg,
|
|
73
|
+
xrunFile: Path.join(subDirPath, xrunFile),
|
|
74
|
+
dir: xrunDir
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// Subdirectory doesn't exist or can't be read, skip it
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Not found anywhere
|
|
83
|
+
const foundPkg = Boolean(dirFiles.find(f => f === "package.json"));
|
|
84
|
+
return {
|
|
85
|
+
found: false,
|
|
86
|
+
foundPkg,
|
|
87
|
+
xrunFile: undefined,
|
|
88
|
+
dir: xrunDir
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { searchUpTaskFile, findTaskFile };
|