@ryanatkn/gro 0.116.1 → 0.117.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/dist/config.d.ts +5 -0
- package/dist/config.js +9 -2
- package/dist/docs/config.md +25 -0
- package/dist/docs/task.md +38 -32
- package/dist/docs/tasks.gen.md.js +2 -2
- package/dist/gen.task.js +5 -6
- package/dist/gen_module.d.ts +2 -1
- package/dist/gen_module.js +1 -1
- package/dist/input_path.d.ts +27 -28
- package/dist/input_path.js +62 -66
- package/dist/input_path.test.js +23 -45
- package/dist/invoke_task.d.ts +1 -1
- package/dist/invoke_task.js +78 -87
- package/dist/modules.d.ts +13 -9
- package/dist/modules.js +5 -13
- package/dist/package.d.ts +11 -11
- package/dist/package.gen.js +3 -3
- package/dist/package.js +41 -38
- package/dist/package_json.js +3 -3
- package/dist/paths.d.ts +11 -10
- package/dist/paths.js +23 -39
- package/dist/publish.task.js +2 -2
- package/dist/run_task.js +2 -2
- package/dist/task.js +1 -0
- package/dist/task_logging.d.ts +8 -0
- package/dist/{print_task.js → task_logging.js} +33 -16
- package/dist/task_module.d.ts +3 -2
- package/dist/task_module.js +5 -6
- package/package.json +20 -20
- package/dist/print_task.d.ts +0 -4
package/dist/config.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ export interface Gro_Config {
|
|
|
8
8
|
* Returning `null` is a no-op for the caller.
|
|
9
9
|
*/
|
|
10
10
|
map_package_json: Map_Package_Json | null;
|
|
11
|
+
/**
|
|
12
|
+
* The root directories to search for tasks given implicit relative input paths.
|
|
13
|
+
* Defaults to `./src/lib`, then the cwd, then the Gro package dist.
|
|
14
|
+
*/
|
|
15
|
+
task_root_paths: string[];
|
|
11
16
|
}
|
|
12
17
|
export interface Create_Gro_Config {
|
|
13
18
|
(base_config: Gro_Config): Gro_Config | Promise<Gro_Config>;
|
package/dist/config.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { CONFIG_PATH, paths } from './paths.js';
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
|
+
import { CONFIG_PATH, GRO_DIST_DIR, IS_THIS_GRO, paths } from './paths.js';
|
|
3
3
|
import create_default_config from './gro.config.default.js';
|
|
4
4
|
import { exists } from './fs.js';
|
|
5
5
|
export const create_empty_config = () => ({
|
|
6
6
|
plugins: () => [],
|
|
7
7
|
// TODO maybe disable if no SvelteKit `lib` directory? or other detection to improve defaults
|
|
8
8
|
map_package_json: default_map_package_json,
|
|
9
|
+
task_root_paths: [paths.lib, paths.root, IS_THIS_GRO ? null : GRO_DIST_DIR].filter(Boolean),
|
|
9
10
|
});
|
|
10
11
|
const default_map_package_json = async (package_json) => {
|
|
11
12
|
if (package_json.exports) {
|
|
@@ -25,12 +26,18 @@ export const load_config = async (dir = paths.root) => {
|
|
|
25
26
|
typeof config_module.default === 'function'
|
|
26
27
|
? await config_module.default(default_config)
|
|
27
28
|
: config_module.default;
|
|
29
|
+
normalize_config(config);
|
|
28
30
|
}
|
|
29
31
|
else {
|
|
30
32
|
config = default_config;
|
|
31
33
|
}
|
|
32
34
|
return config;
|
|
33
35
|
};
|
|
36
|
+
// Mutates `config` with cleaned up values.
|
|
37
|
+
const normalize_config = (config) => {
|
|
38
|
+
// TODO any validation?
|
|
39
|
+
config.task_root_paths = config.task_root_paths.map((p) => resolve(p));
|
|
40
|
+
};
|
|
34
41
|
export const validate_config_module = (config_module, config_path) => {
|
|
35
42
|
const config = config_module.default;
|
|
36
43
|
if (!config) {
|
package/dist/docs/config.md
CHANGED
|
@@ -44,6 +44,7 @@ export interface Create_Gro_Config {
|
|
|
44
44
|
export interface Gro_Config {
|
|
45
45
|
plugins: Create_Config_Plugins;
|
|
46
46
|
map_package_json: Map_Package_Json | null;
|
|
47
|
+
task_root_paths: string[];
|
|
47
48
|
}
|
|
48
49
|
```
|
|
49
50
|
|
|
@@ -80,6 +81,20 @@ const config: Create_Gro_Config = async (cfg) => {
|
|
|
80
81
|
export default config;
|
|
81
82
|
```
|
|
82
83
|
|
|
84
|
+
You can also export a config object and use `create_empty_config` to get the defaults:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import {create_empty_config} from '@ryanatkn/gro/config.js';
|
|
88
|
+
|
|
89
|
+
const config = create_empty_config();
|
|
90
|
+
|
|
91
|
+
// config.plugins = ...;
|
|
92
|
+
// config.map_package_json = ...;
|
|
93
|
+
// config.task_root_paths = ...;
|
|
94
|
+
|
|
95
|
+
export default config;
|
|
96
|
+
```
|
|
97
|
+
|
|
83
98
|
See also [Gro's own internal config](/gro.config.ts).
|
|
84
99
|
|
|
85
100
|
## `plugins`
|
|
@@ -160,3 +175,13 @@ export interface Map_Package_Json {
|
|
|
160
175
|
(package_json: Package_Json): Package_Json | null | Promise<Package_Json | null>;
|
|
161
176
|
}
|
|
162
177
|
```
|
|
178
|
+
|
|
179
|
+
## `task_root_paths`
|
|
180
|
+
|
|
181
|
+
The Gro config option `task_root_paths` allows customizing Gro's task resolution.
|
|
182
|
+
When calling `gro [input_path]`, absolute and explicitly relative paths starting with `.`
|
|
183
|
+
are resolved according to normal filesystem rules,
|
|
184
|
+
but non-explicit input paths, like `foo`, are resolved by searching
|
|
185
|
+
through `task_root_paths` in order until a matching file or directory is found on the filesystem.
|
|
186
|
+
|
|
187
|
+
The default task paths are `./src/lib`, then `.`, and then Gro's dist directory.
|
package/dist/docs/task.md
CHANGED
|
@@ -21,30 +21,29 @@ and defers composition to the user in regular TypeScript modules.
|
|
|
21
21
|
> use the `gro run` task, which works like the normal `node` CLI
|
|
22
22
|
> but uses the Gro loader to support `.ts`.
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
- tasks are defined by naming files with the `.task.ts` and `.task.js` suffixes
|
|
25
|
+
- tasks can be run from the CLI via a name (`gro foo`),
|
|
26
|
+
which uses Gro's task resolution (see more below),
|
|
27
|
+
or via paths that are absolute (`gro /path/to/foo`) or explicitly relative (`gro ./foo`)
|
|
28
|
+
- Gro automatically discovers all `*.task.ts|js` files
|
|
29
|
+
in its configurable directory, so creating a new task
|
|
30
|
+
is as simple as [creating a new file](#define-a-task), no config needed
|
|
31
|
+
(defaults to `src/lib`, see the config option [`task_root_paths`](./config.md#task_root_paths))
|
|
32
|
+
- to view [the available tasks](https://github.com/ryanatkn/gro/blob/main/src/lib/docs/tasks.md)
|
|
33
|
+
run `gro` with no arguments
|
|
28
34
|
- task definitions are just objects with an async `run` function and some optional properties,
|
|
29
35
|
so composing tasks is explicit in your code, just like any other module
|
|
30
|
-
(but there's also the helper `invoke_task
|
|
31
|
-
-
|
|
32
|
-
so running them is as simple as `gro <task>`,
|
|
33
|
-
and in code the task object's `run` function has access to CLI args;
|
|
34
|
-
to view [the available tasks](https://github.com/ryanatkn/gro/blob/main/src/lib/docs/tasks.md)
|
|
35
|
-
run `gro` with no arguments
|
|
36
|
+
(but there's also the helper `invoke_task`, see more below)
|
|
37
|
+
- the task object's `run` function has access to CLI args
|
|
36
38
|
- tasks optionally use [zod](https://github.com/colinhacks/zod) schemas
|
|
37
39
|
for `args` types, runtime parsing with helpful validation errors,
|
|
38
|
-
and
|
|
39
|
-
- it's easy to
|
|
40
|
-
like [`gro test`](/src/lib/test.task.ts) and [`gro gen`](/src/lib/gen.task.ts)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- the
|
|
45
|
-
for both development/build tasks and one-off script authoring/execution;
|
|
46
|
-
it attempts to be portable and extensibile, but there's a _lot_ of room for improvement
|
|
47
|
-
- it's fast because it imports only the modules that your chosen tasks need
|
|
40
|
+
and generated help docs (`gro foo --help`), with DRY co-located definitions
|
|
41
|
+
- it's easy to call into or override any of Gro's builtin tasks,
|
|
42
|
+
like [`gro test`](/src/lib/test.task.ts) and [`gro gen`](/src/lib/gen.task.ts) -
|
|
43
|
+
your own versions with the same name take precedence, and you can invoke the base
|
|
44
|
+
tasks using the `gro/` prefix, e.g. `gro gro/test`
|
|
45
|
+
(tasks are also copy-paste friendly! just update the imports)
|
|
46
|
+
- it's fast because it imports only the modules imported by your invoked tasks, not every task's
|
|
48
47
|
|
|
49
48
|
The task runner's purpose is to provide an ergonomic interface
|
|
50
49
|
between the CLI, build tools, and app code.
|
|
@@ -55,18 +54,24 @@ As a developer, it's nice to be able to reuse TypeScript modules in every contex
|
|
|
55
54
|
### show all available tasks
|
|
56
55
|
|
|
57
56
|
```bash
|
|
58
|
-
# This looks through `src
|
|
59
|
-
#
|
|
57
|
+
# This looks through `src/lib` in both the current working directory and Gro's source
|
|
58
|
+
# for all files matching `*.task.ts|js` and logs them out with their args docs.
|
|
60
59
|
$ gro
|
|
61
60
|
```
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
The [config](./config.md) option [task_root_paths](./config.md#task_root_paths)
|
|
63
|
+
tells Gro where to search for tasks.
|
|
64
|
+
|
|
65
|
+
> Currently, only the first directory specified in `task_root_paths` that's found on the filesystem
|
|
66
|
+
> will be used to automatically discover tasks, like when running `gro` without args.
|
|
67
|
+
> Please open an issue if you would like to see Gro be able to discover
|
|
68
|
+
> tasks in more than one directory - it will take some reworking of internals
|
|
69
|
+
> but it seems like the right design.
|
|
65
70
|
|
|
66
71
|
### show tasks in a directory
|
|
67
72
|
|
|
68
73
|
```bash
|
|
69
|
-
# Logs all `*.task.ts` files in `src/lib/some/dir` and `gro/src/lib/some/dir`.
|
|
74
|
+
# Logs all `*.task.ts|js` files in `src/lib/some/dir` and `gro/src/lib/some/dir`.
|
|
70
75
|
# If no tasks are found, it displays an error.
|
|
71
76
|
$ gro some/dir
|
|
72
77
|
```
|
|
@@ -329,7 +334,7 @@ export const task: Task = {
|
|
|
329
334
|
|
|
330
335
|
## why?
|
|
331
336
|
|
|
332
|
-
Gro usage on the command line (`gro <
|
|
337
|
+
Gro usage on the command line (`gro <task_name_or_directory> [...flags]`)
|
|
333
338
|
looks a lot like using `node`.
|
|
334
339
|
What makes Gro different?
|
|
335
340
|
|
|
@@ -337,14 +342,15 @@ What makes Gro different?
|
|
|
337
342
|
contains task modules that conform to some interface.
|
|
338
343
|
This allows them to be discoverable by convention,
|
|
339
344
|
so running `gro` displays them all without any config, and it puts generic handles on them,
|
|
340
|
-
enabling various verbs (e.g. `run`) and
|
|
341
|
-
|
|
345
|
+
enabling various verbs (e.g. `run`) and
|
|
346
|
+
structured metadata (e.g. `summary` and args schemas for docs and validation).
|
|
347
|
+
- Tasks aren't just a script on the filesystem, they can be composed and inspected in code.
|
|
342
348
|
Task modules do not have any side effects when imported,
|
|
343
349
|
while Node scripts just execute when imported -
|
|
344
|
-
their primary purpose is to cause side effects.
|
|
350
|
+
their primary purpose is to cause side effects, and they're limited to the filesystem API.
|
|
345
351
|
This is useful in many cases - for example `gro taskname --help`
|
|
346
352
|
inspects the args schema and other metadata to print help to the console,
|
|
347
|
-
and `gro` prints the `summary` property of each task it
|
|
353
|
+
and `gro` prints the `summary` property of each task it discovers.
|
|
348
354
|
There's lots more to explore here, like task composition
|
|
349
355
|
and improved DX with new capabilities.
|
|
350
356
|
- Tasks support CLI args that are validated and typesafe
|
|
@@ -353,8 +359,8 @@ What makes Gro different?
|
|
|
353
359
|
- When a task name is given to Gro,
|
|
354
360
|
it first searches `src/lib/` in the current working directory and
|
|
355
361
|
falls back to searching the Gro directory.
|
|
356
|
-
This allows your code and CLI commands to
|
|
357
|
-
or override
|
|
362
|
+
This allows your code and CLI commands to compose Gro's builtin tasks
|
|
363
|
+
or override them without changing how you invoke them.
|
|
358
364
|
Gro reserves no special behavior for its own commands -
|
|
359
365
|
`gro test`, `gro gen`, and all the rest are just tasks that all follow the same rules.
|
|
360
366
|
(see its task at [`src/lib/test.task.ts`](/src/lib/test.task.ts)).
|
|
@@ -4,7 +4,7 @@ import { strip_start } from '@ryanatkn/belt/string.js';
|
|
|
4
4
|
import { to_output_file_name } from '../gen.js';
|
|
5
5
|
import { paths, base_path_to_source_id } from '../paths.js';
|
|
6
6
|
import { load_task_modules } from '../task_module.js';
|
|
7
|
-
import { log_error_reasons } from '../
|
|
7
|
+
import { log_error_reasons } from '../task_logging.js';
|
|
8
8
|
// This is the first simple implementation of Gro's automated docs.
|
|
9
9
|
// It combines Gro's gen and task systems
|
|
10
10
|
// to generate a markdown file describing all of the project's tasks.
|
|
@@ -15,7 +15,7 @@ import { log_error_reasons } from '../print_task.js';
|
|
|
15
15
|
// TODO needs some cleanup and better APIs - paths are confusing and verbose!
|
|
16
16
|
// TODO add backlinks to every document that links to this one
|
|
17
17
|
export const gen = async ({ origin_id, log }) => {
|
|
18
|
-
const result = await load_task_modules();
|
|
18
|
+
const result = await load_task_modules([paths.lib]);
|
|
19
19
|
if (!result.ok) {
|
|
20
20
|
log_error_reasons(log, result.reasons);
|
|
21
21
|
throw new Error(result.type);
|
package/dist/gen.task.js
CHANGED
|
@@ -7,14 +7,14 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
7
7
|
import { Task_Error } from './task.js';
|
|
8
8
|
import { run_gen } from './run_gen.js';
|
|
9
9
|
import { load_gen_module, check_gen_modules, find_gen_modules } from './gen_module.js';
|
|
10
|
-
import {
|
|
10
|
+
import { Raw_Input_Path, to_input_paths } from './input_path.js';
|
|
11
11
|
import { load_modules } from './modules.js';
|
|
12
12
|
import { format_file } from './format_file.js';
|
|
13
|
-
import { print_path } from './paths.js';
|
|
14
|
-
import { log_error_reasons } from './
|
|
13
|
+
import { paths, print_path } from './paths.js';
|
|
14
|
+
import { log_error_reasons } from './task_logging.js';
|
|
15
15
|
export const Args = z
|
|
16
16
|
.object({
|
|
17
|
-
_: z.array(
|
|
17
|
+
_: z.array(Raw_Input_Path, { description: 'paths to generate' }).default([]),
|
|
18
18
|
check: z
|
|
19
19
|
.boolean({ description: 'exit with a nonzero code if any files need to be generated' })
|
|
20
20
|
.default(false),
|
|
@@ -27,8 +27,7 @@ export const task = {
|
|
|
27
27
|
Args,
|
|
28
28
|
run: async ({ args, log, timings }) => {
|
|
29
29
|
const { _: raw_input_paths, check } = args;
|
|
30
|
-
|
|
31
|
-
const input_paths = resolve_input_paths(raw_input_paths);
|
|
30
|
+
const input_paths = raw_input_paths.length ? to_input_paths(raw_input_paths) : [paths.source];
|
|
32
31
|
// load all of the gen modules
|
|
33
32
|
const find_modules_result = await find_gen_modules(input_paths);
|
|
34
33
|
if (!find_modules_result.ok) {
|
package/dist/gen_module.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Module_Meta, type Load_Module_Result, type Find_Modules_Result } from './modules.js';
|
|
2
2
|
import type { Gen, Gen_Results, Gen_File } from './gen.js';
|
|
3
|
+
import { Input_Path } from './input_path.js';
|
|
3
4
|
export declare const GEN_FILE_PATTERN_TEXT = "gen";
|
|
4
5
|
export declare const GEN_FILE_PATTERN: string;
|
|
5
6
|
export declare const is_gen_path: (path: string) => boolean;
|
|
@@ -43,4 +44,4 @@ export type Check_Gen_Module_Result = {
|
|
|
43
44
|
};
|
|
44
45
|
export declare const check_gen_modules: (gen_results: Gen_Results) => Promise<Check_Gen_Module_Result[]>;
|
|
45
46
|
export declare const check_gen_module: (file: Gen_File) => Promise<Check_Gen_Module_Result>;
|
|
46
|
-
export declare const find_gen_modules: (input_paths?:
|
|
47
|
+
export declare const find_gen_modules: (input_paths?: Input_Path[], extensions?: string[], root_dirs?: string[]) => Promise<Find_Modules_Result>;
|
package/dist/gen_module.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { load_module, find_modules, } from './modules.js';
|
|
3
|
-
import { get_possible_source_ids } from './input_path.js';
|
|
3
|
+
import { Input_Path, get_possible_source_ids } from './input_path.js';
|
|
4
4
|
import { paths } from './paths.js';
|
|
5
5
|
import { search_fs } from './search_fs.js';
|
|
6
6
|
import { exists } from './fs.js';
|
package/dist/input_path.d.ts
CHANGED
|
@@ -1,48 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Flavored } from '@ryanatkn/belt/types.js';
|
|
3
|
+
import { Source_Id } from './paths.js';
|
|
1
4
|
import { type Path_Data } from './path.js';
|
|
5
|
+
export declare const Input_Path: z.ZodString;
|
|
6
|
+
export type Input_Path = Flavored<z.infer<typeof Input_Path>, 'Input_Path'>;
|
|
7
|
+
export declare const Raw_Input_Path: z.ZodString;
|
|
8
|
+
export type Raw_Input_Path = Flavored<z.infer<typeof Raw_Input_Path>, 'Raw_Input_Path'>;
|
|
2
9
|
/**
|
|
3
|
-
* Raw input paths are paths that users provide to Gro to reference files
|
|
4
|
-
* enhanced with Gro's conventions like `.test.`, `.task.`, and `.gen.`.
|
|
10
|
+
* Raw input paths are paths that users provide to Gro to reference files for tasks and gen.
|
|
5
11
|
*
|
|
6
|
-
* A raw input path can be:
|
|
12
|
+
* A raw input path can be to a file or directory in the following forms:
|
|
7
13
|
*
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* - any of the above but leading with `gro/` to ignore the local directory
|
|
13
|
-
* - an absolute path to a file or directory in the current directory or Gro's
|
|
14
|
+
* - an absolute path, preserved
|
|
15
|
+
* - an explicit relative path, e.g. `./src/foo`, resolved to `root_path` which defaults to the cwd
|
|
16
|
+
* - an implicit relative path, e.g. `src/foo`, preserved
|
|
17
|
+
* - an implicit relative path prefixed with `gro/`, transformed to absolute in the Gro directory
|
|
14
18
|
*
|
|
15
|
-
*
|
|
16
|
-
* That means that the caller can look for `.test.` files but not `.gen.`,
|
|
17
|
-
* or both, or neither, depending on its needs.
|
|
18
|
-
*
|
|
19
|
-
* In the future we may want to support globbing or regexps.
|
|
19
|
+
* Thus, input paths are either absolute or implicitly relative.
|
|
20
20
|
*/
|
|
21
|
-
export declare const
|
|
22
|
-
export declare const
|
|
21
|
+
export declare const to_input_path: (raw_input_path: Flavored<string, "Raw_Input_Path">, root_path?: string) => Flavored<string, "Input_Path">;
|
|
22
|
+
export declare const to_input_paths: (raw_input_paths: Raw_Input_Path[], root_path?: string) => Input_Path[];
|
|
23
23
|
/**
|
|
24
24
|
* Gets a list of possible source ids for each input path with `extensions`,
|
|
25
25
|
* duplicating each under `root_dirs`.
|
|
26
26
|
* This is first used to fall back to the Gro dir to search for tasks.
|
|
27
27
|
* It's the helper used in implementations of `get_possible_source_ids_for_input_path` below.
|
|
28
28
|
*/
|
|
29
|
-
export declare const get_possible_source_ids: (input_path: string, extensions: string[], root_dirs?: string[]) =>
|
|
29
|
+
export declare const get_possible_source_ids: (input_path: Flavored<string, "Input_Path">, extensions: string[], root_dirs?: string[]) => Source_Id[];
|
|
30
30
|
/**
|
|
31
|
-
* Gets the path data for each input path,
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Parameterized by `exists` and `stat` so it's fs-agnostic.
|
|
31
|
+
* Gets the path data for each input path, checking the filesystem for the possibilities
|
|
32
|
+
* and stopping at the first existing file or falling back to the first existing directory.
|
|
33
|
+
* If none is found for an input path, it's added to `unmapped_input_paths`.
|
|
35
34
|
*/
|
|
36
|
-
export declare const load_source_path_data_by_input_path: (input_paths:
|
|
37
|
-
source_id_path_data_by_input_path: Map<
|
|
38
|
-
unmapped_input_paths:
|
|
35
|
+
export declare const load_source_path_data_by_input_path: (input_paths: Input_Path[], get_possible_source_ids_for_input_path?: (input_path: Input_Path) => Source_Id[]) => Promise<{
|
|
36
|
+
source_id_path_data_by_input_path: Map<Input_Path, Path_Data>;
|
|
37
|
+
unmapped_input_paths: Input_Path[];
|
|
39
38
|
}>;
|
|
40
39
|
/**
|
|
41
40
|
* Finds all of the matching files for the given input paths.
|
|
42
|
-
* Parameterized by `find_files` so it's fs-agnostic.
|
|
43
41
|
* De-dupes source ids.
|
|
44
42
|
*/
|
|
45
|
-
export declare const load_source_ids_by_input_path: (source_id_path_data_by_input_path: Map<
|
|
46
|
-
source_ids_by_input_path: Map<
|
|
47
|
-
input_directories_with_no_files:
|
|
43
|
+
export declare const load_source_ids_by_input_path: (source_id_path_data_by_input_path: Map<Input_Path, Path_Data>, custom_search_fs?: (dir: string, options?: import("./search_fs.js").Search_Fs_Options) => Promise<Map<string, import("./path.js").Path_Stats>>) => Promise<{
|
|
44
|
+
source_ids_by_input_path: Map<Input_Path, Source_Id[]>;
|
|
45
|
+
input_directories_with_no_files: Input_Path[];
|
|
48
46
|
}>;
|
|
47
|
+
export declare const to_gro_input_path: (input_path: Flavored<string, "Input_Path">) => Flavored<string, "Input_Path">;
|
package/dist/input_path.js
CHANGED
|
@@ -1,48 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
2
|
+
import { strip_start } from '@ryanatkn/belt/string.js';
|
|
3
3
|
import { stat } from 'node:fs/promises';
|
|
4
|
-
import {
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { GRO_PACKAGE_DIR, GRO_DIST_DIR, paths, Source_Id } from './paths.js';
|
|
5
6
|
import { to_path_data } from './path.js';
|
|
6
7
|
import { exists } from './fs.js';
|
|
7
8
|
import { search_fs } from './search_fs.js';
|
|
9
|
+
import { TASK_FILE_SUFFIX_JS } from './task.js';
|
|
10
|
+
// TODO Flavored doesn't work when used in schemas, use Zod brand instead? problem is ergonomics
|
|
11
|
+
export const Input_Path = z.string();
|
|
12
|
+
export const Raw_Input_Path = z.string();
|
|
8
13
|
/**
|
|
9
|
-
* Raw input paths are paths that users provide to Gro to reference files
|
|
10
|
-
* enhanced with Gro's conventions like `.test.`, `.task.`, and `.gen.`.
|
|
14
|
+
* Raw input paths are paths that users provide to Gro to reference files for tasks and gen.
|
|
11
15
|
*
|
|
12
|
-
* A raw input path can be:
|
|
16
|
+
* A raw input path can be to a file or directory in the following forms:
|
|
13
17
|
*
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
* - any of the above but leading with `gro/` to ignore the local directory
|
|
19
|
-
* - an absolute path to a file or directory in the current directory or Gro's
|
|
18
|
+
* - an absolute path, preserved
|
|
19
|
+
* - an explicit relative path, e.g. `./src/foo`, resolved to `root_path` which defaults to the cwd
|
|
20
|
+
* - an implicit relative path, e.g. `src/foo`, preserved
|
|
21
|
+
* - an implicit relative path prefixed with `gro/`, transformed to absolute in the Gro directory
|
|
20
22
|
*
|
|
21
|
-
*
|
|
22
|
-
* That means that the caller can look for `.test.` files but not `.gen.`,
|
|
23
|
-
* or both, or neither, depending on its needs.
|
|
24
|
-
*
|
|
25
|
-
* In the future we may want to support globbing or regexps.
|
|
23
|
+
* Thus, input paths are either absolute or implicitly relative.
|
|
26
24
|
*/
|
|
27
|
-
export const
|
|
28
|
-
if (
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// If it's prefixed with `gro/` or exactly `gro`, use the Gro paths.
|
|
34
|
-
if (is_this_project_gro || (base_path + '/').startsWith(gro_dir_basename)) {
|
|
35
|
-
paths = gro_paths;
|
|
36
|
-
base_path = strip_end(strip_start(base_path + '/', gro_dir_basename), '/');
|
|
25
|
+
export const to_input_path = (raw_input_path, root_path = process.cwd()) => {
|
|
26
|
+
if (raw_input_path.startsWith(GRO_PACKAGE_DIR)) {
|
|
27
|
+
return GRO_DIST_DIR + strip_start(raw_input_path, GRO_PACKAGE_DIR);
|
|
28
|
+
}
|
|
29
|
+
else if (raw_input_path[0] === '.') {
|
|
30
|
+
return resolve(root_path, raw_input_path);
|
|
37
31
|
}
|
|
38
|
-
|
|
39
|
-
if (base_path === LIB_PATH)
|
|
40
|
-
base_path = ''; // TODO @multiple get from the sveltekit config
|
|
41
|
-
// Allow prefix `src/lib/` and just remove it if it's there.
|
|
42
|
-
base_path = strip_start(base_path, LIB_DIR);
|
|
43
|
-
return lib_path_to_import_id(base_path, paths);
|
|
32
|
+
return raw_input_path;
|
|
44
33
|
};
|
|
45
|
-
export const
|
|
34
|
+
export const to_input_paths = (raw_input_paths, root_path) => raw_input_paths.map((p) => to_input_path(p, root_path));
|
|
46
35
|
/**
|
|
47
36
|
* Gets a list of possible source ids for each input path with `extensions`,
|
|
48
37
|
* duplicating each under `root_dirs`.
|
|
@@ -50,39 +39,37 @@ export const resolve_input_paths = (raw_input_paths) => raw_input_paths?.length
|
|
|
50
39
|
* It's the helper used in implementations of `get_possible_source_ids_for_input_path` below.
|
|
51
40
|
*/
|
|
52
41
|
export const get_possible_source_ids = (input_path, extensions, root_dirs) => {
|
|
53
|
-
const possible_source_ids = [
|
|
54
|
-
|
|
55
|
-
for
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
const possible_source_ids = [];
|
|
43
|
+
const add_possible_source_ids = (path) => {
|
|
44
|
+
// Specifically for paths to the Gro package dist, optimize by only looking for `.task.js`.
|
|
45
|
+
if (path.startsWith(GRO_DIST_DIR)) {
|
|
46
|
+
possible_source_ids.push((path.endsWith('/') || path.endsWith(TASK_FILE_SUFFIX_JS)
|
|
47
|
+
? path
|
|
48
|
+
: path + TASK_FILE_SUFFIX_JS));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
possible_source_ids.push(path);
|
|
52
|
+
if (!path.endsWith('/') && !extensions.some((e) => path.endsWith(e))) {
|
|
53
|
+
for (const extension of extensions) {
|
|
54
|
+
possible_source_ids.push(path + extension);
|
|
55
|
+
}
|
|
60
56
|
}
|
|
61
57
|
}
|
|
58
|
+
};
|
|
59
|
+
if (isAbsolute(input_path)) {
|
|
60
|
+
add_possible_source_ids(input_path);
|
|
62
61
|
}
|
|
63
|
-
if (root_dirs?.length) {
|
|
64
|
-
const ids = possible_source_ids.slice(); // make a copy or infinitely loop!
|
|
62
|
+
else if (root_dirs?.length) {
|
|
65
63
|
for (const root_dir of root_dirs) {
|
|
66
|
-
|
|
67
|
-
continue; // avoid duplicates
|
|
68
|
-
const is_gro_dist = root_dir === gro_sveltekit_dist_dir; // TODO hacky to handle Gro importing its JS tasks from dist/
|
|
69
|
-
for (const possible_source_id of ids) {
|
|
70
|
-
if (is_gro_dist && !possible_source_id.endsWith('.js'))
|
|
71
|
-
continue;
|
|
72
|
-
// TODO hacky to handle Gro importing its JS tasks from dist/
|
|
73
|
-
possible_source_ids.push(is_gro_dist
|
|
74
|
-
? gro_sveltekit_dist_dir + strip_start(possible_source_id, paths.lib)
|
|
75
|
-
: replace_root_dir(possible_source_id, root_dir, paths));
|
|
76
|
-
}
|
|
64
|
+
add_possible_source_ids(join(root_dir, input_path));
|
|
77
65
|
}
|
|
78
66
|
}
|
|
79
67
|
return possible_source_ids;
|
|
80
68
|
};
|
|
81
69
|
/**
|
|
82
|
-
* Gets the path data for each input path,
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* Parameterized by `exists` and `stat` so it's fs-agnostic.
|
|
70
|
+
* Gets the path data for each input path, checking the filesystem for the possibilities
|
|
71
|
+
* and stopping at the first existing file or falling back to the first existing directory.
|
|
72
|
+
* If none is found for an input path, it's added to `unmapped_input_paths`.
|
|
86
73
|
*/
|
|
87
74
|
export const load_source_path_data_by_input_path = async (input_paths, get_possible_source_ids_for_input_path) => {
|
|
88
75
|
const source_id_path_data_by_input_path = new Map();
|
|
@@ -93,22 +80,24 @@ export const load_source_path_data_by_input_path = async (input_paths, get_possi
|
|
|
93
80
|
const possible_source_ids = get_possible_source_ids_for_input_path
|
|
94
81
|
? get_possible_source_ids_for_input_path(input_path)
|
|
95
82
|
: [input_path];
|
|
83
|
+
// Find the first existing file path or fallback to the first directory path.
|
|
96
84
|
for (const possible_source_id of possible_source_ids) {
|
|
97
85
|
if (!(await exists(possible_source_id)))
|
|
98
86
|
continue; // eslint-disable-line no-await-in-loop
|
|
99
87
|
const stats = await stat(possible_source_id); // eslint-disable-line no-await-in-loop
|
|
100
88
|
if (stats.isDirectory()) {
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
if (dir_path_data)
|
|
90
|
+
continue;
|
|
91
|
+
dir_path_data = to_path_data(possible_source_id, stats);
|
|
104
92
|
}
|
|
105
93
|
else {
|
|
106
94
|
file_path_data = to_path_data(possible_source_id, stats);
|
|
107
95
|
break;
|
|
108
96
|
}
|
|
109
97
|
}
|
|
110
|
-
|
|
111
|
-
|
|
98
|
+
const path_data = file_path_data || dir_path_data;
|
|
99
|
+
if (path_data) {
|
|
100
|
+
source_id_path_data_by_input_path.set(input_path, path_data);
|
|
112
101
|
}
|
|
113
102
|
else {
|
|
114
103
|
unmapped_input_paths.push(input_path);
|
|
@@ -118,7 +107,6 @@ export const load_source_path_data_by_input_path = async (input_paths, get_possi
|
|
|
118
107
|
};
|
|
119
108
|
/**
|
|
120
109
|
* Finds all of the matching files for the given input paths.
|
|
121
|
-
* Parameterized by `find_files` so it's fs-agnostic.
|
|
122
110
|
* De-dupes source ids.
|
|
123
111
|
*/
|
|
124
112
|
export const load_source_ids_by_input_path = async (source_id_path_data_by_input_path, custom_search_fs = search_fs) => {
|
|
@@ -132,7 +120,9 @@ export const load_source_ids_by_input_path = async (source_id_path_data_by_input
|
|
|
132
120
|
if (files.size) {
|
|
133
121
|
const source_ids = [];
|
|
134
122
|
let has_files = false;
|
|
135
|
-
for (const path of files
|
|
123
|
+
for (const [path, stats] of files) {
|
|
124
|
+
if (stats.isDirectory())
|
|
125
|
+
continue;
|
|
136
126
|
has_files = true;
|
|
137
127
|
const source_id = join(id, path);
|
|
138
128
|
if (!existing_source_ids.has(source_id)) {
|
|
@@ -146,7 +136,7 @@ export const load_source_ids_by_input_path = async (source_id_path_data_by_input
|
|
|
146
136
|
if (!has_files) {
|
|
147
137
|
input_directories_with_no_files.push(input_path);
|
|
148
138
|
}
|
|
149
|
-
// do callers ever need `
|
|
139
|
+
// do callers ever need `input_directories_with_duplicate_files`?
|
|
150
140
|
}
|
|
151
141
|
else {
|
|
152
142
|
input_directories_with_no_files.push(input_path);
|
|
@@ -159,3 +149,9 @@ export const load_source_ids_by_input_path = async (source_id_path_data_by_input
|
|
|
159
149
|
}
|
|
160
150
|
return { source_ids_by_input_path, input_directories_with_no_files };
|
|
161
151
|
};
|
|
152
|
+
// TODO I don't think this is valid any more, we shouldn't transform absolute paths like this,
|
|
153
|
+
// the searching should happen with the input paths
|
|
154
|
+
export const to_gro_input_path = (input_path) => {
|
|
155
|
+
const base_path = input_path === paths.lib.slice(0, -1) ? '' : strip_start(input_path, paths.lib);
|
|
156
|
+
return GRO_DIST_DIR + base_path;
|
|
157
|
+
};
|