@ryanatkn/gro 0.116.2 → 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 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) {
@@ -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
- - Gro automatically discovers [all `*.task.ts` files](../docs/tasks.md)
25
- in your source directory, so creating a new task
26
- is as simple as [creating a new file](#define-a-task), no config needed,
27
- but part of the convention is that Gro expects all source code to be in `src/`
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`: see more below)
31
- - on the command line, tasks replace the concept of commands,
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 automatic help output, with DRY co-located definitions
39
- - it's easy to hook into or override any of Gro's builtin tasks,
40
- like [`gro test`](/src/lib/test.task.ts) and [`gro gen`](/src/lib/gen.task.ts)
41
- (tasks are copy-paste friendly! just update the imports)
42
- - the task execution environment is filesystem agnostic by default; `run` receives a
43
- [`Task_Context` argument](#user-content-types-task-and-taskcontext) with an `fs` property
44
- - the `Task_Context` provides a rich baseline context object
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/` in both the current working directory
59
- # and Gro's source for all files matching `*.task.ts` and logs them out.
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
- > notice that Gro is hardcoded to expect all tasks to be in `src/` --
64
- > it would be nice to loosen this up, but the API isn't obvious to me right now
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 <taskOrDirectory> [...flags]`)
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 structured metadata (e.g. `summary`).
341
- - Tasks aren't just a script, they can be inspected, composed, and manipulated in code.
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 finds.
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 use Gro's builtin tasks
357
- or override or extend them without changing how you invoke them.
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 '../print_task.js';
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 { resolve_input_paths } from './input_path.js';
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 './print_task.js';
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(z.string(), { description: 'paths to generate' }).default([]),
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
- // resolve the input paths relative to src/lib/
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) {
@@ -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?: string[], extensions?: string[], root_dirs?: string[]) => Promise<Find_Modules_Result>;
47
+ export declare const find_gen_modules: (input_paths?: Input_Path[], extensions?: string[], root_dirs?: string[]) => Promise<Find_Modules_Result>;
@@ -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';
@@ -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
- * - a relative path to a file, e.g. `src/foo/bar.test.ts`
9
- * - a file without an extension, e.g. `src/foo/bar` if `extensions` is `.test.ts`
10
- * - a directory containing any number of files, e.g. `src/foo`
11
- * - any of the above without the leading `src/` or with a leading `./`
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
- * The input path API lets the caller customize the allowable extensions.
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 resolve_input_path: (raw_input_path: string) => string;
22
- export declare const resolve_input_paths: (raw_input_paths?: string[]) => string[];
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[]) => 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
- * searching for the possibilities based on `extensions`
33
- * and stopping at the first match.
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: string[], get_possible_source_ids_for_input_path?: (input_path: string) => string[]) => Promise<{
37
- source_id_path_data_by_input_path: Map<string, Path_Data>;
38
- unmapped_input_paths: string[];
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<string, Path_Data>, custom_search_fs?: (dir: string, options?: import("./search_fs.js").Search_Fs_Options) => Promise<Map<string, import("./path.js").Path_Stats>>) => Promise<{
46
- source_ids_by_input_path: Map<string, string[]>;
47
- input_directories_with_no_files: string[];
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">;
@@ -1,48 +1,37 @@
1
- import { join, isAbsolute, basename } from 'node:path';
2
- import { strip_end, strip_start } from '@ryanatkn/belt/string.js';
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 { lib_path_to_import_id, replace_root_dir, gro_dir_basename, gro_paths, LIB_DIR, LIB_PATH, is_this_project_gro, gro_sveltekit_dist_dir, paths, } from './paths.js';
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
- * - a relative path to a file, e.g. `src/foo/bar.test.ts`
15
- * - a file without an extension, e.g. `src/foo/bar` if `extensions` is `.test.ts`
16
- * - a directory containing any number of files, e.g. `src/foo`
17
- * - any of the above without the leading `src/` or with a leading `./`
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
- * The input path API lets the caller customize the allowable extensions.
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 resolve_input_path = (raw_input_path) => {
28
- if (isAbsolute(raw_input_path))
29
- return strip_end(raw_input_path, '/');
30
- // Allow prefix `./` and just remove it if it's there.
31
- let base_path = strip_end(strip_start(raw_input_path, './'), '/');
32
- let paths;
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
- // Handle `src/lib` by itself without conflicting with `src/libFoo` names.
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 resolve_input_paths = (raw_input_paths) => raw_input_paths?.length ? raw_input_paths.map((p) => resolve_input_path(p)) : [paths.source];
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 = [input_path];
54
- if (!input_path.endsWith('/')) {
55
- for (const extension of extensions) {
56
- if (!input_path.endsWith(extension)) {
57
- possible_source_ids.push(input_path + extension);
58
- // Support task directories, so `src/lib/a/a.task.ts` works like `src/a.task.ts`.
59
- possible_source_ids.push(join(input_path, basename(input_path) + extension));
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
- if (input_path.startsWith(root_dir))
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
- * searching for the possibilities based on `extensions`
84
- * and stopping at the first match.
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 (!dir_path_data) {
102
- dir_path_data = to_path_data(possible_source_id, stats);
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
- if (file_path_data || dir_path_data) {
111
- source_id_path_data_by_input_path.set(input_path, file_path_data || dir_path_data); // the ! is needed because TypeScript inference fails
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.keys()) {
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 `inputDirectoriesWithDuplicateFiles`?
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
+ };