@ryanatkn/gro 0.121.1 → 0.123.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/README.md +1 -1
- package/dist/config.js +7 -11
- package/dist/docs/tasks.md +1 -1
- package/dist/gen.d.ts +1 -6
- package/dist/gen.js +3 -8
- package/dist/gen.test.js +0 -1
- package/dist/gro_plugin_gen.js +1 -1
- package/dist/input_path.d.ts +2 -4
- package/dist/input_path.js +39 -51
- package/dist/input_path.test.js +54 -37
- package/dist/package.js +10 -10
- package/dist/path_constants.js +1 -1
- package/dist/resolve.task.js +1 -1
- package/dist/task.d.ts +3 -8
- package/dist/task.js +10 -11
- package/dist/task.test.js +9 -6
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ lint run eslint
|
|
|
122
122
|
publish bump version, publish to npm, and git push
|
|
123
123
|
reinstall refreshes package-lock.json with the latest and cleanest deps
|
|
124
124
|
release publish and deploy
|
|
125
|
-
resolve
|
|
125
|
+
resolve diagnostic that logs resolved filesystem info for the given input paths
|
|
126
126
|
run execute a file with the loader, like `node` but works for TypeScript
|
|
127
127
|
sync run `gro gen`, update `package.json`, and optionally `npm i` to sync up
|
|
128
128
|
test run tests with uvu
|
package/dist/config.js
CHANGED
|
@@ -20,17 +20,13 @@ export const create_empty_config = () => ({
|
|
|
20
20
|
* Customize via `search_filters` in the `Gro_Config`.
|
|
21
21
|
* See the test cases for the exact behavior.
|
|
22
22
|
*/
|
|
23
|
-
export const DEFAULT_SEARCH_EXCLUDER = new RegExp(`(${[
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
SERVER_DIST_PATH,
|
|
31
|
-
]
|
|
32
|
-
.map((p) => '(^|/)' + p)
|
|
33
|
-
.join('|')})($|/)`, 'u');
|
|
23
|
+
export const DEFAULT_SEARCH_EXCLUDER = new RegExp(`(${'(^|/)\\.[^/]+' + // exclude all `.`-prefixed directories
|
|
24
|
+
// TODO probably change to `pkg.name` instead of this catch-all (also `gro` below)
|
|
25
|
+
`|(^|/)${NODE_MODULES_DIRNAME}(?!/(@[^/]+/)?gro/${SVELTEKIT_DIST_DIRNAME})` + // exclude `node_modules` unless it's to the Gro directory
|
|
26
|
+
`|(^|/)${SVELTEKIT_BUILD_DIRNAME}` + // exclude the SvelteKit build directory
|
|
27
|
+
`|(^|/)(?<!(^|/)gro/)${SVELTEKIT_DIST_DIRNAME}` + // exclude the SvelteKit dist directory unless it's in the Gro directory
|
|
28
|
+
`|(^|/)${SERVER_DIST_PATH}` // exclude the Gro server plugin dist directory
|
|
29
|
+
})($|/)`, 'u');
|
|
34
30
|
const default_map_package_json = async (package_json) => {
|
|
35
31
|
if (package_json.exports) {
|
|
36
32
|
package_json.exports = Object.fromEntries(Object.entries(package_json.exports).filter(([k]) => !DEFAULT_EXPORTS_EXCLUDER.test(k)));
|
package/dist/docs/tasks.md
CHANGED
|
@@ -19,7 +19,7 @@ What is a `Task`? See [`task.md`](./task.md).
|
|
|
19
19
|
- [publish](../publish.task.ts) - bump version, publish to npm, and git push
|
|
20
20
|
- [reinstall](../reinstall.task.ts) - refreshes package-lock.json with the latest and cleanest deps
|
|
21
21
|
- [release](../release.task.ts) - publish and deploy
|
|
22
|
-
- [resolve](../resolve.task.ts) - diagnostic that logs
|
|
22
|
+
- [resolve](../resolve.task.ts) - diagnostic that logs resolved filesystem info for the given input paths
|
|
23
23
|
- [run](../run.task.ts) - execute a file with the loader, like `node` but works for TypeScript
|
|
24
24
|
- [sync](../sync.task.ts) - run `gro gen`, update `package.json`, and optionally `npm i` to sync up
|
|
25
25
|
- [test](../test.task.ts) - run tests with uvu
|
package/dist/gen.d.ts
CHANGED
|
@@ -85,10 +85,8 @@ export declare const analyze_gen_result: (file: Gen_File) => Promise<Analyzed_Ge
|
|
|
85
85
|
export declare const write_gen_results: (gen_results: Gen_Results, analyzed_gen_results: Analyzed_Gen_Result[], log: Logger) => Promise<void>;
|
|
86
86
|
export interface Found_Genfiles {
|
|
87
87
|
resolved_input_files: Resolved_Input_File[];
|
|
88
|
-
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
89
88
|
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
90
89
|
resolved_input_paths: Resolved_Input_Path[];
|
|
91
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
92
90
|
}
|
|
93
91
|
export type Find_Genfiles_Result = Result<{
|
|
94
92
|
value: Found_Genfiles;
|
|
@@ -97,16 +95,13 @@ export type Find_Genfiles_Failure = {
|
|
|
97
95
|
type: 'unmapped_input_paths';
|
|
98
96
|
unmapped_input_paths: Input_Path[];
|
|
99
97
|
resolved_input_paths: Resolved_Input_Path[];
|
|
100
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
101
98
|
reasons: string[];
|
|
102
99
|
} | {
|
|
103
100
|
type: 'input_directories_with_no_files';
|
|
104
|
-
input_directories_with_no_files:
|
|
101
|
+
input_directories_with_no_files: Input_Path[];
|
|
105
102
|
resolved_input_files: Resolved_Input_File[];
|
|
106
|
-
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
107
103
|
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
108
104
|
resolved_input_paths: Resolved_Input_Path[];
|
|
109
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
110
105
|
reasons: string[];
|
|
111
106
|
};
|
|
112
107
|
/**
|
package/dist/gen.js
CHANGED
|
@@ -136,7 +136,7 @@ export const find_genfiles = async (input_paths, root_dirs, config, timings) =>
|
|
|
136
136
|
const extensions = [GEN_FILE_PATTERN];
|
|
137
137
|
// Check which extension variation works - if it's a directory, prefer others first!
|
|
138
138
|
const timing_to_resolve_input_paths = timings?.start('resolve input paths');
|
|
139
|
-
const { resolved_input_paths,
|
|
139
|
+
const { resolved_input_paths, unmapped_input_paths } = resolve_input_paths(input_paths, root_dirs, extensions);
|
|
140
140
|
timing_to_resolve_input_paths?.();
|
|
141
141
|
// Error if any input path could not be mapped.
|
|
142
142
|
if (unmapped_input_paths.length) {
|
|
@@ -145,13 +145,12 @@ export const find_genfiles = async (input_paths, root_dirs, config, timings) =>
|
|
|
145
145
|
type: 'unmapped_input_paths',
|
|
146
146
|
unmapped_input_paths,
|
|
147
147
|
resolved_input_paths,
|
|
148
|
-
resolved_input_paths_by_input_path,
|
|
149
148
|
reasons: unmapped_input_paths.map((input_path) => red(`Input path ${print_path(input_path)} cannot be mapped to a file or directory.`)),
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
151
|
// Find all of the files for any directories.
|
|
153
152
|
const timing_to_search_fs = timings?.start('find files');
|
|
154
|
-
const { resolved_input_files,
|
|
153
|
+
const { resolved_input_files, resolved_input_files_by_root_dir, input_directories_with_no_files } = resolve_input_files(resolved_input_paths, (id) => search_fs(id, {
|
|
155
154
|
filter: config.search_filters,
|
|
156
155
|
file_filter: (p) => extensions.some((e) => p.includes(e)),
|
|
157
156
|
}));
|
|
@@ -163,21 +162,17 @@ export const find_genfiles = async (input_paths, root_dirs, config, timings) =>
|
|
|
163
162
|
type: 'input_directories_with_no_files',
|
|
164
163
|
input_directories_with_no_files,
|
|
165
164
|
resolved_input_files,
|
|
166
|
-
resolved_input_files_by_input_path,
|
|
167
165
|
resolved_input_files_by_root_dir,
|
|
168
166
|
resolved_input_paths,
|
|
169
|
-
|
|
170
|
-
reasons: input_directories_with_no_files.map(({ input_path }) => red(`Input directory contains no matching files: ${print_path(input_path)}`)),
|
|
167
|
+
reasons: input_directories_with_no_files.map((input_path) => red(`Input directory contains no matching files: ${print_path(input_path)}`)),
|
|
171
168
|
};
|
|
172
169
|
}
|
|
173
170
|
return {
|
|
174
171
|
ok: true,
|
|
175
172
|
value: {
|
|
176
173
|
resolved_input_files,
|
|
177
|
-
resolved_input_files_by_input_path,
|
|
178
174
|
resolved_input_files_by_root_dir,
|
|
179
175
|
resolved_input_paths,
|
|
180
|
-
resolved_input_paths_by_input_path,
|
|
181
176
|
},
|
|
182
177
|
};
|
|
183
178
|
};
|
package/dist/gen.test.js
CHANGED
|
@@ -247,6 +247,5 @@ test('find_genfiles_result finds gen modules in a directory', async () => {
|
|
|
247
247
|
const find_genfiles_result = await find_genfiles(['docs'], [paths.lib], create_empty_config());
|
|
248
248
|
assert.ok(find_genfiles_result.ok);
|
|
249
249
|
assert.ok(find_genfiles_result.value.resolved_input_paths.length);
|
|
250
|
-
assert.ok(find_genfiles_result.value.resolved_input_paths_by_input_path.size);
|
|
251
250
|
});
|
|
252
251
|
test.run();
|
package/dist/gro_plugin_gen.js
CHANGED
|
@@ -45,7 +45,7 @@ export const plugin = () => {
|
|
|
45
45
|
// making us miss `build` events for gen dependencies,
|
|
46
46
|
// so we run `gen` here even if it's usually wasteful.
|
|
47
47
|
const found = await find_genfiles([paths.source], root_dirs, config);
|
|
48
|
-
if (found.ok && found.
|
|
48
|
+
if (found.ok && found.value.resolved_input_files.size > 0) {
|
|
49
49
|
await gen();
|
|
50
50
|
}
|
|
51
51
|
// Do we need to just generate everything once and exit?
|
package/dist/input_path.d.ts
CHANGED
|
@@ -42,7 +42,6 @@ export interface Resolved_Input_File {
|
|
|
42
42
|
}
|
|
43
43
|
export interface Resolved_Input_Paths {
|
|
44
44
|
resolved_input_paths: Resolved_Input_Path[];
|
|
45
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
46
45
|
possible_paths_by_input_path: Map<Input_Path, Possible_Path[]>;
|
|
47
46
|
unmapped_input_paths: Input_Path[];
|
|
48
47
|
}
|
|
@@ -54,12 +53,11 @@ export interface Resolved_Input_Paths {
|
|
|
54
53
|
export declare const resolve_input_paths: (input_paths: Input_Path[], root_dirs: Path_Id[], extensions: string[]) => Resolved_Input_Paths;
|
|
55
54
|
export interface Resolved_Input_Files {
|
|
56
55
|
resolved_input_files: Resolved_Input_File[];
|
|
57
|
-
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
58
56
|
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
59
|
-
input_directories_with_no_files:
|
|
57
|
+
input_directories_with_no_files: Input_Path[];
|
|
60
58
|
}
|
|
61
59
|
/**
|
|
62
60
|
* Finds all of the matching files for the given input paths.
|
|
63
61
|
* De-dupes source ids.
|
|
64
62
|
*/
|
|
65
|
-
export declare const resolve_input_files: (resolved_input_paths: Resolved_Input_Path[],
|
|
63
|
+
export declare const resolve_input_files: (resolved_input_paths: Resolved_Input_Path[], search?: (dir: string) => Resolved_Path[]) => Resolved_Input_Files;
|
package/dist/input_path.js
CHANGED
|
@@ -122,15 +122,6 @@ export const resolve_input_paths = (input_paths, root_dirs, extensions) => {
|
|
|
122
122
|
}
|
|
123
123
|
return {
|
|
124
124
|
resolved_input_paths,
|
|
125
|
-
resolved_input_paths_by_input_path: resolved_input_paths.reduce((map, resolved_input_path) => {
|
|
126
|
-
if (map.has(resolved_input_path.input_path)) {
|
|
127
|
-
map.get(resolved_input_path.input_path).push(resolved_input_path);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
map.set(resolved_input_path.input_path, [resolved_input_path]);
|
|
131
|
-
}
|
|
132
|
-
return map;
|
|
133
|
-
}, new Map()),
|
|
134
125
|
possible_paths_by_input_path,
|
|
135
126
|
unmapped_input_paths,
|
|
136
127
|
};
|
|
@@ -139,61 +130,58 @@ export const resolve_input_paths = (input_paths, root_dirs, extensions) => {
|
|
|
139
130
|
* Finds all of the matching files for the given input paths.
|
|
140
131
|
* De-dupes source ids.
|
|
141
132
|
*/
|
|
142
|
-
export const resolve_input_files = (resolved_input_paths,
|
|
133
|
+
export const resolve_input_files = (resolved_input_paths, search = search_fs) => {
|
|
143
134
|
const resolved_input_files = [];
|
|
144
|
-
|
|
145
|
-
const input_directories_with_no_files = [];
|
|
135
|
+
// Add all input paths initially, and remove each when resolved to a file.
|
|
146
136
|
const existing_path_ids = new Set();
|
|
137
|
+
let remaining = resolved_input_paths.slice();
|
|
138
|
+
const handle_found = (input_path, id) => {
|
|
139
|
+
remaining = remaining.filter((r) => !(r.id === id || r.input_path === input_path || r.input_path === id));
|
|
140
|
+
};
|
|
147
141
|
// TODO parallelize but would need to de-dupe and retain order
|
|
148
142
|
for (const resolved_input_path of resolved_input_paths) {
|
|
149
143
|
const { input_path, id, is_directory } = resolved_input_path;
|
|
150
144
|
if (is_directory) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (path_ids.length) {
|
|
166
|
-
const resolved_input_files_for_input_path = [];
|
|
167
|
-
for (const path_id of path_ids) {
|
|
168
|
-
const resolved_input_file = {
|
|
169
|
-
id: path_id,
|
|
170
|
-
input_path,
|
|
171
|
-
resolved_input_path,
|
|
172
|
-
};
|
|
173
|
-
resolved_input_files.push(resolved_input_file);
|
|
174
|
-
resolved_input_files_for_input_path.push(resolved_input_file);
|
|
175
|
-
}
|
|
176
|
-
resolved_input_files_by_input_path.set(input_path, resolved_input_files_for_input_path);
|
|
177
|
-
}
|
|
178
|
-
if (!has_files) {
|
|
179
|
-
input_directories_with_no_files.push(resolved_input_path);
|
|
145
|
+
// Handle input paths that resolve to directories.
|
|
146
|
+
const files = search(id);
|
|
147
|
+
if (!files.length)
|
|
148
|
+
continue;
|
|
149
|
+
const path_ids = [];
|
|
150
|
+
for (const { path, is_directory } of files) {
|
|
151
|
+
if (is_directory)
|
|
152
|
+
continue;
|
|
153
|
+
const path_id = join(id, path);
|
|
154
|
+
if (!existing_path_ids.has(path_id)) {
|
|
155
|
+
existing_path_ids.add(path_id);
|
|
156
|
+
path_ids.push(path_id);
|
|
180
157
|
}
|
|
181
|
-
|
|
158
|
+
handle_found(input_path, path_id);
|
|
182
159
|
}
|
|
183
|
-
|
|
184
|
-
|
|
160
|
+
if (!path_ids.length)
|
|
161
|
+
continue;
|
|
162
|
+
const resolved_input_files_for_input_path = [];
|
|
163
|
+
for (const path_id of path_ids) {
|
|
164
|
+
const resolved_input_file = {
|
|
165
|
+
id: path_id,
|
|
166
|
+
input_path,
|
|
167
|
+
resolved_input_path,
|
|
168
|
+
};
|
|
169
|
+
resolved_input_files.push(resolved_input_file);
|
|
170
|
+
resolved_input_files_for_input_path.push(resolved_input_file);
|
|
185
171
|
}
|
|
186
172
|
}
|
|
187
|
-
else
|
|
188
|
-
existing_path_ids.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
173
|
+
else {
|
|
174
|
+
if (!existing_path_ids.has(id)) {
|
|
175
|
+
// Handle input paths that resolve to files.
|
|
176
|
+
existing_path_ids.add(id);
|
|
177
|
+
const resolved_input_file = { id, input_path, resolved_input_path };
|
|
178
|
+
resolved_input_files.push(resolved_input_file);
|
|
179
|
+
}
|
|
180
|
+
handle_found(input_path, id);
|
|
192
181
|
}
|
|
193
182
|
}
|
|
194
183
|
return {
|
|
195
184
|
resolved_input_files,
|
|
196
|
-
resolved_input_files_by_input_path,
|
|
197
185
|
resolved_input_files_by_root_dir: resolved_input_files.reduce((map, resolved_input_file) => {
|
|
198
186
|
const { root_dir } = resolved_input_file.resolved_input_path;
|
|
199
187
|
if (map.has(root_dir)) {
|
|
@@ -204,6 +192,6 @@ export const resolve_input_files = (resolved_input_paths, custom_search_fs = sea
|
|
|
204
192
|
}
|
|
205
193
|
return map;
|
|
206
194
|
}, new Map()),
|
|
207
|
-
input_directories_with_no_files,
|
|
195
|
+
input_directories_with_no_files: remaining.map((r) => r.input_path),
|
|
208
196
|
};
|
|
209
197
|
};
|
package/dist/input_path.test.js
CHANGED
|
@@ -92,25 +92,30 @@ test('get_possible_paths implied to be a directory by trailing slash', () => {
|
|
|
92
92
|
});
|
|
93
93
|
test('resolve_input_files', async () => {
|
|
94
94
|
const test_files = {
|
|
95
|
-
'fake/test1.ext.ts': [
|
|
96
|
-
|
|
95
|
+
'fake/test1.ext.ts': [
|
|
96
|
+
{ id: 'fake/test1.ext.ts', path: 'fake/test1.ext.ts', is_directory: false },
|
|
97
|
+
],
|
|
98
|
+
'fake/test2.ext.ts': [
|
|
99
|
+
{ id: 'fake/test2.ext.ts', path: 'fake/test2.ext.ts', is_directory: false },
|
|
100
|
+
],
|
|
97
101
|
'fake/test3': [
|
|
98
|
-
{ id: '', path: 'fake/test3', is_directory: true },
|
|
99
|
-
{ id: '', path: 'a.ts', is_directory: false },
|
|
100
|
-
{ id: '', path: 'b.ts', is_directory: false },
|
|
102
|
+
{ id: 'fake/test3', path: 'fake/test3', is_directory: true },
|
|
103
|
+
{ id: 'a.ts', path: 'a.ts', is_directory: false },
|
|
104
|
+
{ id: 'b.ts', path: 'b.ts', is_directory: false },
|
|
101
105
|
],
|
|
102
106
|
// duplicate
|
|
103
107
|
'fake/': [
|
|
104
|
-
{ id: '', path: 'fake/test3', is_directory: true },
|
|
105
|
-
{ id: '', path: 'test3/a.ts', is_directory: false },
|
|
108
|
+
{ id: 'fake/test3', path: 'fake/test3', is_directory: true },
|
|
109
|
+
{ id: 'test3/a.ts', path: 'test3/a.ts', is_directory: false },
|
|
106
110
|
],
|
|
107
111
|
// duplicate and not
|
|
108
112
|
fake: [
|
|
109
|
-
{ id: '', path: 'fake/test3', is_directory: true },
|
|
110
|
-
{ id: '', path: 'test3/a.ts', is_directory: false },
|
|
111
|
-
{ id: '', path: 'test3/c.ts', is_directory: false },
|
|
113
|
+
{ id: 'fake/test3', path: 'fake/test3', is_directory: true },
|
|
114
|
+
{ id: 'test3/a.ts', path: 'test3/a.ts', is_directory: false },
|
|
115
|
+
{ id: 'test3/c.ts', path: 'test3/c.ts', is_directory: false },
|
|
112
116
|
],
|
|
113
|
-
'fake/nomatches': [{ id: '', path: 'fake/nomatches', is_directory: true }],
|
|
117
|
+
'fake/nomatches': [{ id: 'fake/nomatches', path: 'fake/nomatches', is_directory: true }],
|
|
118
|
+
fake2: [{ id: 'test.ext.ts', path: 'test.ext.ts', is_directory: false }],
|
|
114
119
|
};
|
|
115
120
|
const a = {
|
|
116
121
|
id: 'fake/test1.ext.ts',
|
|
@@ -131,58 +136,70 @@ test('resolve_input_files', async () => {
|
|
|
131
136
|
root_dir: process.cwd(),
|
|
132
137
|
};
|
|
133
138
|
const d = {
|
|
134
|
-
id: 'fake
|
|
139
|
+
id: 'fake',
|
|
135
140
|
is_directory: true,
|
|
136
|
-
input_path: 'fake
|
|
141
|
+
input_path: 'fake',
|
|
137
142
|
root_dir: process.cwd(),
|
|
138
143
|
};
|
|
139
144
|
const e = {
|
|
140
|
-
id: 'fake',
|
|
145
|
+
id: 'fake/nomatches',
|
|
141
146
|
is_directory: true,
|
|
142
|
-
input_path: 'fake',
|
|
147
|
+
input_path: 'fake/nomatches',
|
|
143
148
|
root_dir: process.cwd(),
|
|
144
149
|
};
|
|
150
|
+
// These two have the same id from different directory input paths.
|
|
145
151
|
const f = {
|
|
146
|
-
id: '
|
|
152
|
+
id: 'fake2',
|
|
147
153
|
is_directory: true,
|
|
148
|
-
input_path: '
|
|
154
|
+
input_path: 'fake2',
|
|
155
|
+
root_dir: process.cwd(),
|
|
156
|
+
};
|
|
157
|
+
const g = {
|
|
158
|
+
id: 'fake2',
|
|
159
|
+
is_directory: true,
|
|
160
|
+
input_path: './fake2/',
|
|
161
|
+
root_dir: process.cwd(),
|
|
162
|
+
};
|
|
163
|
+
// These two have the same id from different file input paths.
|
|
164
|
+
const h = {
|
|
165
|
+
id: 'fake3/test.ext.ts',
|
|
166
|
+
is_directory: false,
|
|
167
|
+
input_path: 'fake3/test.ext.ts',
|
|
149
168
|
root_dir: process.cwd(),
|
|
150
169
|
};
|
|
151
|
-
const
|
|
170
|
+
const i = {
|
|
171
|
+
id: 'fake3/test.ext.ts',
|
|
172
|
+
is_directory: false,
|
|
173
|
+
input_path: 'fake3/test',
|
|
174
|
+
root_dir: process.cwd(),
|
|
175
|
+
};
|
|
176
|
+
const result = resolve_input_files([a, b, c, d, e, f, g, h, i], (dir) => test_files[dir]);
|
|
152
177
|
const resolved_input_files = [
|
|
153
178
|
{ id: a.id, input_path: a.input_path, resolved_input_path: a },
|
|
154
179
|
{ id: b.id, input_path: b.input_path, resolved_input_path: b },
|
|
155
180
|
{ id: 'fake/test3/a.ts', input_path: c.input_path, resolved_input_path: c },
|
|
156
181
|
{ id: 'fake/test3/b.ts', input_path: c.input_path, resolved_input_path: c },
|
|
157
|
-
{ id: 'fake/test3/c.ts', input_path:
|
|
182
|
+
{ id: 'fake/test3/c.ts', input_path: d.input_path, resolved_input_path: d },
|
|
183
|
+
{ id: 'fake2/test.ext.ts', input_path: f.input_path, resolved_input_path: f },
|
|
184
|
+
{ id: 'fake3/test.ext.ts', input_path: h.input_path, resolved_input_path: h },
|
|
158
185
|
];
|
|
159
186
|
assert.equal(result, {
|
|
160
187
|
resolved_input_files,
|
|
161
|
-
resolved_input_files_by_input_path: new Map([
|
|
162
|
-
['fake/test1.ext.ts', [{ id: a.id, input_path: a.input_path, resolved_input_path: a }]],
|
|
163
|
-
['fake/test2', [{ id: b.id, input_path: b.input_path, resolved_input_path: b }]],
|
|
164
|
-
[
|
|
165
|
-
'fake/test3',
|
|
166
|
-
[
|
|
167
|
-
{ id: 'fake/test3/a.ts', input_path: c.input_path, resolved_input_path: c },
|
|
168
|
-
{ id: 'fake/test3/b.ts', input_path: c.input_path, resolved_input_path: c },
|
|
169
|
-
],
|
|
170
|
-
],
|
|
171
|
-
['fake', [{ id: 'fake/test3/c.ts', input_path: e.input_path, resolved_input_path: e }]],
|
|
172
|
-
]),
|
|
173
188
|
resolved_input_files_by_root_dir: new Map([
|
|
174
189
|
[
|
|
175
190
|
process.cwd(),
|
|
176
191
|
[
|
|
177
|
-
{ id: 'fake/test1.ext.ts', input_path:
|
|
178
|
-
{ id: 'fake/test2.ext.ts', input_path:
|
|
179
|
-
{ id: 'fake/test3/a.ts', input_path:
|
|
180
|
-
{ id: 'fake/test3/b.ts', input_path:
|
|
181
|
-
{ id: 'fake/test3/c.ts', input_path:
|
|
192
|
+
{ id: 'fake/test1.ext.ts', input_path: a.input_path, resolved_input_path: a },
|
|
193
|
+
{ id: 'fake/test2.ext.ts', input_path: b.input_path, resolved_input_path: b },
|
|
194
|
+
{ id: 'fake/test3/a.ts', input_path: c.input_path, resolved_input_path: c },
|
|
195
|
+
{ id: 'fake/test3/b.ts', input_path: c.input_path, resolved_input_path: c },
|
|
196
|
+
{ id: 'fake/test3/c.ts', input_path: d.input_path, resolved_input_path: d },
|
|
197
|
+
{ id: 'fake2/test.ext.ts', input_path: f.input_path, resolved_input_path: f },
|
|
198
|
+
{ id: 'fake3/test.ext.ts', input_path: h.input_path, resolved_input_path: h },
|
|
182
199
|
],
|
|
183
200
|
],
|
|
184
201
|
]),
|
|
185
|
-
input_directories_with_no_files: [
|
|
202
|
+
input_directories_with_no_files: [e.input_path],
|
|
186
203
|
});
|
|
187
204
|
});
|
|
188
205
|
test.run();
|
package/dist/package.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// generated by src/lib/package.gen.ts
|
|
2
2
|
export const package_json = {
|
|
3
3
|
name: '@ryanatkn/gro',
|
|
4
|
-
version: '0.
|
|
4
|
+
version: '0.123.0',
|
|
5
5
|
description: 'task runner and toolkit extending SvelteKit',
|
|
6
6
|
motto: 'generate, run, optimize',
|
|
7
7
|
icon: '🌰',
|
|
@@ -33,10 +33,10 @@ export const package_json = {
|
|
|
33
33
|
],
|
|
34
34
|
files: ['dist'],
|
|
35
35
|
dependencies: {
|
|
36
|
-
'@ryanatkn/belt': '^0.
|
|
36
|
+
'@ryanatkn/belt': '^0.21.0',
|
|
37
37
|
chokidar: '^3.6.0',
|
|
38
38
|
dotenv: '^16.4.5',
|
|
39
|
-
'es-module-lexer': '^1.5.
|
|
39
|
+
'es-module-lexer': '^1.5.4',
|
|
40
40
|
kleur: '^4.1.5',
|
|
41
41
|
mri: '^1.2.0',
|
|
42
42
|
prettier: '^3.3.2',
|
|
@@ -50,21 +50,21 @@ export const package_json = {
|
|
|
50
50
|
'@changesets/changelog-git': '^0.2.0',
|
|
51
51
|
'@changesets/types': '^6.0.0',
|
|
52
52
|
'@ryanatkn/eslint-config': '^0.1.3',
|
|
53
|
-
'@ryanatkn/fuz': '^0.
|
|
54
|
-
'@ryanatkn/moss': '^0.
|
|
53
|
+
'@ryanatkn/fuz': '^0.104.1',
|
|
54
|
+
'@ryanatkn/moss': '^0.5.0',
|
|
55
55
|
'@sveltejs/adapter-static': '^3.0.2',
|
|
56
56
|
'@sveltejs/kit': '^2.5.17',
|
|
57
57
|
'@sveltejs/package': '^2.3.2',
|
|
58
58
|
'@sveltejs/vite-plugin-svelte': '^3.1.1',
|
|
59
59
|
'@types/fs-extra': '^11.0.4',
|
|
60
60
|
'@types/node': '^20.14.8',
|
|
61
|
-
'@typescript-eslint/eslint-plugin': '^7.
|
|
62
|
-
'@typescript-eslint/parser': '^7.
|
|
61
|
+
'@typescript-eslint/eslint-plugin': '^7.14.1',
|
|
62
|
+
'@typescript-eslint/parser': '^7.14.1',
|
|
63
63
|
esbuild: '^0.20.2',
|
|
64
64
|
eslint: '^8.57.0',
|
|
65
|
-
'eslint-plugin-svelte': '^2.
|
|
65
|
+
'eslint-plugin-svelte': '^2.41.0',
|
|
66
66
|
svelte: '^5.0.0-next.164',
|
|
67
|
-
'svelte-check': '^3.8.
|
|
67
|
+
'svelte-check': '^3.8.2',
|
|
68
68
|
typescript: '^5.5.2',
|
|
69
69
|
uvu: '^0.5.6',
|
|
70
70
|
},
|
|
@@ -256,7 +256,7 @@ export const package_json = {
|
|
|
256
256
|
};
|
|
257
257
|
export const src_json = {
|
|
258
258
|
name: '@ryanatkn/gro',
|
|
259
|
-
version: '0.
|
|
259
|
+
version: '0.123.0',
|
|
260
260
|
modules: {
|
|
261
261
|
'.': {
|
|
262
262
|
path: 'index.ts',
|
package/dist/path_constants.js
CHANGED
|
@@ -8,7 +8,7 @@ If any of these become customizable from SvelteKit or Gro's configs, move them t
|
|
|
8
8
|
export const SOURCE_DIRNAME = 'src';
|
|
9
9
|
export const GRO_DIRNAME = '.gro';
|
|
10
10
|
export const GRO_DIST_PREFIX = 'dist_'; //
|
|
11
|
-
export const SERVER_DIST_PATH = 'dist_server'; // TODO should all of these be `_PATH` or should this be `DIRNAME`?
|
|
11
|
+
export const SERVER_DIST_PATH = 'dist_server'; // TODO should all of these be `_PATH` or should this be `DIRNAME`? also, add `_PLUGIN` to this name?
|
|
12
12
|
export const GRO_DEV_DIRNAME = GRO_DIRNAME + '/dev';
|
|
13
13
|
export const SOURCE_DIR = SOURCE_DIRNAME + '/';
|
|
14
14
|
export const GRO_DIR = GRO_DIRNAME + '/';
|
package/dist/resolve.task.js
CHANGED
|
@@ -7,7 +7,7 @@ export const Args = z
|
|
|
7
7
|
})
|
|
8
8
|
.strict();
|
|
9
9
|
export const task = {
|
|
10
|
-
summary: 'diagnostic that logs
|
|
10
|
+
summary: 'diagnostic that logs resolved filesystem info for the given input paths',
|
|
11
11
|
Args,
|
|
12
12
|
run: async ({ args, config, log }) => {
|
|
13
13
|
const { _ } = args;
|
package/dist/task.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const TASK_FILE_SUFFIX_TS = ".task.ts";
|
|
|
26
26
|
export declare const TASK_FILE_SUFFIX_JS = ".task.js";
|
|
27
27
|
export declare const TASK_FILE_SUFFIXES: string[];
|
|
28
28
|
export declare const is_task_path: (path: string) => boolean;
|
|
29
|
-
export declare const to_task_name: (id: Path_Id, task_root_dir: Path_Id) => string;
|
|
29
|
+
export declare const to_task_name: (id: Path_Id, task_root_dir: Path_Id, input_path: Input_Path, root_path: Path_Id) => string;
|
|
30
30
|
/**
|
|
31
31
|
* This is used by tasks to signal a known failure.
|
|
32
32
|
* It's useful for cleaning up logging because
|
|
@@ -41,10 +41,8 @@ export interface Found_Task {
|
|
|
41
41
|
}
|
|
42
42
|
export interface Found_Tasks {
|
|
43
43
|
resolved_input_files: Resolved_Input_File[];
|
|
44
|
-
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
45
44
|
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
46
45
|
resolved_input_paths: Resolved_Input_Path[];
|
|
47
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
48
46
|
input_paths: Input_Path[];
|
|
49
47
|
task_root_dirs: Path_Id[];
|
|
50
48
|
}
|
|
@@ -55,18 +53,15 @@ export type Find_Modules_Failure = {
|
|
|
55
53
|
type: 'unmapped_input_paths';
|
|
56
54
|
unmapped_input_paths: Input_Path[];
|
|
57
55
|
resolved_input_paths: Resolved_Input_Path[];
|
|
58
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
59
56
|
input_paths: Input_Path[];
|
|
60
57
|
task_root_dirs: Path_Id[];
|
|
61
58
|
reasons: string[];
|
|
62
59
|
} | {
|
|
63
60
|
type: 'input_directories_with_no_files';
|
|
64
|
-
input_directories_with_no_files:
|
|
61
|
+
input_directories_with_no_files: Input_Path[];
|
|
65
62
|
resolved_input_files: Resolved_Input_File[];
|
|
66
|
-
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
67
63
|
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
68
64
|
resolved_input_paths: Resolved_Input_Path[];
|
|
69
|
-
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
70
65
|
input_paths: Input_Path[];
|
|
71
66
|
task_root_dirs: Path_Id[];
|
|
72
67
|
reasons: string[];
|
|
@@ -89,5 +84,5 @@ export type Load_Tasks_Result = Result<{
|
|
|
89
84
|
value: Loaded_Tasks;
|
|
90
85
|
}, Load_Tasks_Failure>;
|
|
91
86
|
export type Load_Tasks_Failure = Load_Modules_Failure<Task_Module_Meta>;
|
|
92
|
-
export declare const load_tasks: (found_tasks: Found_Tasks) => Promise<Load_Tasks_Result>;
|
|
87
|
+
export declare const load_tasks: (found_tasks: Found_Tasks, root_path?: Path_Id) => Promise<Load_Tasks_Result>;
|
|
93
88
|
export declare const validate_task_module: (mod: Record<string, any>) => mod is Task_Module;
|
package/dist/task.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { strip_end, strip_start } from '@ryanatkn/belt/string.js';
|
|
2
2
|
import { red } from 'kleur/colors';
|
|
3
|
+
import { isAbsolute, join, relative } from 'node:path';
|
|
3
4
|
import { resolve_input_files, resolve_input_paths, } from './input_path.js';
|
|
4
5
|
import { print_path } from './paths.js';
|
|
5
6
|
import { search_fs } from './search_fs.js';
|
|
@@ -8,13 +9,16 @@ export const TASK_FILE_SUFFIX_TS = '.task.ts';
|
|
|
8
9
|
export const TASK_FILE_SUFFIX_JS = '.task.js';
|
|
9
10
|
export const TASK_FILE_SUFFIXES = [TASK_FILE_SUFFIX_TS, TASK_FILE_SUFFIX_JS]; // TODO from `Gro_Config`, but needs to be used everywhere the constants are
|
|
10
11
|
export const is_task_path = (path) => path.endsWith(TASK_FILE_SUFFIX_TS) || path.endsWith(TASK_FILE_SUFFIX_JS);
|
|
11
|
-
export const to_task_name = (id, task_root_dir) => {
|
|
12
|
+
export const to_task_name = (id, task_root_dir, input_path, root_path) => {
|
|
12
13
|
let task_name = id.startsWith(task_root_dir)
|
|
13
14
|
? strip_start(strip_start(id, task_root_dir), '/')
|
|
14
15
|
: id;
|
|
15
16
|
for (const suffix of TASK_FILE_SUFFIXES) {
|
|
16
17
|
task_name = strip_end(task_name, suffix);
|
|
17
18
|
}
|
|
19
|
+
if (isAbsolute(input_path)) {
|
|
20
|
+
return relative(root_path, join(input_path, task_name));
|
|
21
|
+
}
|
|
18
22
|
return task_name;
|
|
19
23
|
};
|
|
20
24
|
/**
|
|
@@ -30,7 +34,7 @@ export class Task_Error extends Error {
|
|
|
30
34
|
export const find_tasks = (input_paths, task_root_dirs, config, timings) => {
|
|
31
35
|
// Check which extension variation works - if it's a directory, prefer others first!
|
|
32
36
|
const timing_to_resolve_input_paths = timings?.start('resolve input paths');
|
|
33
|
-
const { resolved_input_paths,
|
|
37
|
+
const { resolved_input_paths, unmapped_input_paths } = resolve_input_paths(input_paths, task_root_dirs, TASK_FILE_SUFFIXES);
|
|
34
38
|
timing_to_resolve_input_paths?.();
|
|
35
39
|
// Error if any input path could not be mapped.
|
|
36
40
|
if (unmapped_input_paths.length) {
|
|
@@ -39,7 +43,6 @@ export const find_tasks = (input_paths, task_root_dirs, config, timings) => {
|
|
|
39
43
|
type: 'unmapped_input_paths',
|
|
40
44
|
unmapped_input_paths,
|
|
41
45
|
resolved_input_paths,
|
|
42
|
-
resolved_input_paths_by_input_path,
|
|
43
46
|
input_paths,
|
|
44
47
|
task_root_dirs,
|
|
45
48
|
reasons: unmapped_input_paths.map((input_path) => red(`Input path ${print_path(input_path)} cannot be mapped to a file or directory.`)),
|
|
@@ -47,7 +50,7 @@ export const find_tasks = (input_paths, task_root_dirs, config, timings) => {
|
|
|
47
50
|
}
|
|
48
51
|
// Find all of the files for any directories.
|
|
49
52
|
const timing_to_resolve_input_files = timings?.start('resolve input files');
|
|
50
|
-
const { resolved_input_files,
|
|
53
|
+
const { resolved_input_files, resolved_input_files_by_root_dir, input_directories_with_no_files } = resolve_input_files(resolved_input_paths, (id) => search_fs(id, {
|
|
51
54
|
filter: config.search_filters,
|
|
52
55
|
file_filter: (p) => TASK_FILE_SUFFIXES.some((s) => p.endsWith(s)),
|
|
53
56
|
}));
|
|
@@ -59,33 +62,29 @@ export const find_tasks = (input_paths, task_root_dirs, config, timings) => {
|
|
|
59
62
|
type: 'input_directories_with_no_files',
|
|
60
63
|
input_directories_with_no_files,
|
|
61
64
|
resolved_input_files,
|
|
62
|
-
resolved_input_files_by_input_path,
|
|
63
65
|
resolved_input_files_by_root_dir,
|
|
64
66
|
resolved_input_paths,
|
|
65
|
-
resolved_input_paths_by_input_path,
|
|
66
67
|
input_paths,
|
|
67
68
|
task_root_dirs,
|
|
68
|
-
reasons: input_directories_with_no_files.map((
|
|
69
|
+
reasons: input_directories_with_no_files.map((input_path) => red(`Input directory contains no matching files: ${print_path(input_path)}`)),
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
72
|
return {
|
|
72
73
|
ok: true,
|
|
73
74
|
value: {
|
|
74
75
|
resolved_input_files,
|
|
75
|
-
resolved_input_files_by_input_path,
|
|
76
76
|
resolved_input_files_by_root_dir,
|
|
77
77
|
resolved_input_paths,
|
|
78
|
-
resolved_input_paths_by_input_path,
|
|
79
78
|
input_paths,
|
|
80
79
|
task_root_dirs,
|
|
81
80
|
},
|
|
82
81
|
};
|
|
83
82
|
};
|
|
84
|
-
export const load_tasks = async (found_tasks) => {
|
|
83
|
+
export const load_tasks = async (found_tasks, root_path = process.cwd()) => {
|
|
85
84
|
const loaded_modules = await load_modules(found_tasks.resolved_input_files, validate_task_module, (resolved_input_file, mod) => ({
|
|
86
85
|
id: resolved_input_file.id,
|
|
87
86
|
mod,
|
|
88
|
-
name: to_task_name(resolved_input_file.id, resolved_input_file.resolved_input_path.root_dir),
|
|
87
|
+
name: to_task_name(resolved_input_file.id, resolved_input_file.resolved_input_path.root_dir, resolved_input_file.resolved_input_path.input_path, root_path),
|
|
89
88
|
}));
|
|
90
89
|
if (!loaded_modules.ok) {
|
|
91
90
|
return loaded_modules;
|
package/dist/task.test.js
CHANGED
|
@@ -12,12 +12,15 @@ test('is_task_path basic behavior', () => {
|
|
|
12
12
|
assert.ok(!is_task_path('bar/baz/foo.ts'));
|
|
13
13
|
});
|
|
14
14
|
test('to_task_name basic behavior', () => {
|
|
15
|
-
assert.is(to_task_name('foo.task.ts', process.cwd()), 'foo');
|
|
16
|
-
assert.is(to_task_name('bar/baz/foo.task.ts', process.cwd()), 'bar/baz/foo');
|
|
17
|
-
assert.is(to_task_name('a/b/c/foo.task.ts', 'a/b/c'), 'foo');
|
|
18
|
-
assert.is(to_task_name('a/b/c/foo.task.ts', 'a'), 'b/c/foo');
|
|
19
|
-
assert.is(to_task_name('a/b/c/foo.task.ts', 'a/b'), 'c/foo');
|
|
20
|
-
assert.is(to_task_name(
|
|
15
|
+
assert.is(to_task_name('foo.task.ts', process.cwd(), '', ''), 'foo');
|
|
16
|
+
assert.is(to_task_name('bar/baz/foo.task.ts', process.cwd(), '', ''), 'bar/baz/foo');
|
|
17
|
+
assert.is(to_task_name('a/b/c/foo.task.ts', 'a/b/c', '', ''), 'foo');
|
|
18
|
+
assert.is(to_task_name('a/b/c/foo.task.ts', 'a', '', ''), 'b/c/foo');
|
|
19
|
+
assert.is(to_task_name('a/b/c/foo.task.ts', 'a/b', '', ''), 'c/foo');
|
|
20
|
+
assert.is(to_task_name('/a/b/c/foo.task.ts', '/a/b', '/a/b', '/a/b/d'), '../c/foo');
|
|
21
|
+
assert.is(to_task_name('/a/b/c/foo.task.ts', '/a/b', '/a/b', '/a/b'), 'c/foo');
|
|
22
|
+
assert.is(to_task_name('/a/b/c/foo.task.ts', '/a/b', '/a/b', '/a/b/c'), 'foo');
|
|
23
|
+
assert.is(to_task_name(resolve('a/b'), resolve('b'), '', ''), resolve('a/b'), 'falls back to the id when unresolved');
|
|
21
24
|
});
|
|
22
25
|
// TODO if we import directly, svelte-package generates types in `src/fixtures`
|
|
23
26
|
const test_task_module = await import('../fixtures/' + 'test_task_module.task_fixture'); // eslint-disable-line no-useless-concat
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryanatkn/gro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.123.0",
|
|
4
4
|
"description": "task runner and toolkit extending SvelteKit",
|
|
5
5
|
"motto": "generate, run, optimize",
|
|
6
6
|
"icon": "🌰",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"dist"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@ryanatkn/belt": "^0.
|
|
48
|
+
"@ryanatkn/belt": "^0.21.0",
|
|
49
49
|
"chokidar": "^3.6.0",
|
|
50
50
|
"dotenv": "^16.4.5",
|
|
51
|
-
"es-module-lexer": "^1.5.
|
|
51
|
+
"es-module-lexer": "^1.5.4",
|
|
52
52
|
"kleur": "^4.1.5",
|
|
53
53
|
"mri": "^1.2.0",
|
|
54
54
|
"prettier": "^3.3.2",
|
|
@@ -65,21 +65,21 @@
|
|
|
65
65
|
"@changesets/changelog-git": "^0.2.0",
|
|
66
66
|
"@changesets/types": "^6.0.0",
|
|
67
67
|
"@ryanatkn/eslint-config": "^0.1.3",
|
|
68
|
-
"@ryanatkn/fuz": "^0.
|
|
69
|
-
"@ryanatkn/moss": "^0.
|
|
68
|
+
"@ryanatkn/fuz": "^0.104.1",
|
|
69
|
+
"@ryanatkn/moss": "^0.5.0",
|
|
70
70
|
"@sveltejs/adapter-static": "^3.0.2",
|
|
71
71
|
"@sveltejs/kit": "^2.5.17",
|
|
72
72
|
"@sveltejs/package": "^2.3.2",
|
|
73
73
|
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
|
74
74
|
"@types/fs-extra": "^11.0.4",
|
|
75
75
|
"@types/node": "^20.14.8",
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
77
|
-
"@typescript-eslint/parser": "^7.
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
|
77
|
+
"@typescript-eslint/parser": "^7.14.1",
|
|
78
78
|
"esbuild": "^0.20.2",
|
|
79
79
|
"eslint": "^8.57.0",
|
|
80
|
-
"eslint-plugin-svelte": "^2.
|
|
80
|
+
"eslint-plugin-svelte": "^2.41.0",
|
|
81
81
|
"svelte": "^5.0.0-next.164",
|
|
82
|
-
"svelte-check": "^3.8.
|
|
82
|
+
"svelte-check": "^3.8.2",
|
|
83
83
|
"typescript": "^5.5.2",
|
|
84
84
|
"uvu": "^0.5.6"
|
|
85
85
|
},
|