@ryanatkn/gro 0.135.2 → 0.135.3
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/check.task.d.ts +2 -2
- package/dist/filer.d.ts +29 -0
- package/dist/filer.d.ts.map +1 -0
- package/dist/filer.js +165 -0
- package/dist/gen.task.d.ts +2 -2
- package/dist/gro.config.default.d.ts.map +1 -1
- package/dist/gro.config.default.js +2 -3
- package/dist/gro_plugin_gen.d.ts +9 -2
- package/dist/gro_plugin_gen.d.ts.map +1 -1
- package/dist/gro_plugin_gen.js +67 -37
- package/dist/gro_plugin_sveltekit_library.d.ts.map +1 -1
- package/dist/gro_plugin_sveltekit_library.js +4 -16
- package/dist/invoke_task.d.ts +2 -1
- package/dist/invoke_task.d.ts.map +1 -1
- package/dist/invoke_task.js +4 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +3 -9
- package/dist/package.d.ts +22 -0
- package/dist/package.d.ts.map +1 -1
- package/dist/package.js +39 -13
- package/dist/parse_imports.d.ts +6 -0
- package/dist/parse_imports.d.ts.map +1 -0
- package/dist/parse_imports.js +29 -0
- package/dist/path_constants.d.ts +1 -0
- package/dist/path_constants.d.ts.map +1 -1
- package/dist/path_constants.js +1 -0
- package/dist/paths.d.ts +2 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +3 -1
- package/dist/run_task.d.ts +2 -1
- package/dist/run_task.d.ts.map +1 -1
- package/dist/run_task.js +4 -3
- package/dist/sveltekit_config.d.ts +1 -1
- package/dist/sveltekit_config.d.ts.map +1 -1
- package/dist/sveltekit_config.js +1 -1
- package/dist/sveltekit_helpers.d.ts +6 -0
- package/dist/sveltekit_helpers.d.ts.map +1 -1
- package/dist/sveltekit_helpers.js +32 -0
- package/dist/sync.task.d.ts +2 -2
- package/dist/task.d.ts +2 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/watch_dir.d.ts +1 -1
- package/dist/watch_dir.d.ts.map +1 -1
- package/dist/watch_dir.js +14 -12
- package/package.json +19 -11
- package/src/lib/filer.ts +212 -0
- package/src/lib/gro.config.default.ts +2 -3
- package/src/lib/gro_plugin_gen.ts +90 -43
- package/src/lib/gro_plugin_sveltekit_library.ts +4 -20
- package/src/lib/invoke_task.ts +6 -1
- package/src/lib/loader.ts +3 -10
- package/src/lib/package.ts +39 -13
- package/src/lib/parse_imports.ts +42 -0
- package/src/lib/path_constants.ts +1 -0
- package/src/lib/paths.ts +8 -1
- package/src/lib/run_task.ts +5 -2
- package/src/lib/sveltekit_config.ts +2 -2
- package/src/lib/sveltekit_helpers.ts +46 -0
- package/src/lib/task.ts +2 -0
- package/src/lib/watch_dir.ts +15 -14
package/dist/check.task.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare const Args: z.ZodObject<{
|
|
|
22
22
|
sync: boolean;
|
|
23
23
|
package_json: boolean;
|
|
24
24
|
format: boolean;
|
|
25
|
+
gen: boolean;
|
|
25
26
|
'no-sync': boolean;
|
|
26
27
|
install: boolean;
|
|
27
28
|
'no-install': boolean;
|
|
@@ -29,7 +30,6 @@ export declare const Args: z.ZodObject<{
|
|
|
29
30
|
'no-typecheck': boolean;
|
|
30
31
|
test: boolean;
|
|
31
32
|
'no-test': boolean;
|
|
32
|
-
gen: boolean;
|
|
33
33
|
'no-gen': boolean;
|
|
34
34
|
'no-format': boolean;
|
|
35
35
|
'no-package_json': boolean;
|
|
@@ -40,6 +40,7 @@ export declare const Args: z.ZodObject<{
|
|
|
40
40
|
sync?: boolean | undefined;
|
|
41
41
|
package_json?: boolean | undefined;
|
|
42
42
|
format?: boolean | undefined;
|
|
43
|
+
gen?: boolean | undefined;
|
|
43
44
|
'no-sync'?: boolean | undefined;
|
|
44
45
|
install?: boolean | undefined;
|
|
45
46
|
'no-install'?: boolean | undefined;
|
|
@@ -47,7 +48,6 @@ export declare const Args: z.ZodObject<{
|
|
|
47
48
|
'no-typecheck'?: boolean | undefined;
|
|
48
49
|
test?: boolean | undefined;
|
|
49
50
|
'no-test'?: boolean | undefined;
|
|
50
|
-
gen?: boolean | undefined;
|
|
51
51
|
'no-gen'?: boolean | undefined;
|
|
52
52
|
'no-format'?: boolean | undefined;
|
|
53
53
|
'no-package_json'?: boolean | undefined;
|
package/dist/filer.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Omit_Strict } from '@ryanatkn/belt/types.js';
|
|
2
|
+
import type { Path_Id } from './path.js';
|
|
3
|
+
import { watch_dir, type Watcher_Change, type Options as Watch_Dir_Options } from './watch_dir.js';
|
|
4
|
+
export interface Source_File {
|
|
5
|
+
id: Path_Id;
|
|
6
|
+
/**
|
|
7
|
+
* `null` contents means it doesn't exist.
|
|
8
|
+
* We create the file in memory to track its dependents regardless of its existence on disk.
|
|
9
|
+
*/
|
|
10
|
+
contents: string | null;
|
|
11
|
+
dependents: Map<Path_Id, Source_File>;
|
|
12
|
+
dependencies: Map<Path_Id, Source_File>;
|
|
13
|
+
}
|
|
14
|
+
export type Cleanup_Watch = () => Promise<void>;
|
|
15
|
+
export type On_Filer_Change = (change: Watcher_Change, source_file: Source_File) => void;
|
|
16
|
+
export interface Options {
|
|
17
|
+
watch_dir?: typeof watch_dir;
|
|
18
|
+
watch_dir_options?: Partial<Omit_Strict<Watch_Dir_Options, 'on_change'>>;
|
|
19
|
+
}
|
|
20
|
+
export declare class Filer {
|
|
21
|
+
#private;
|
|
22
|
+
files: Map<Path_Id, Source_File>;
|
|
23
|
+
constructor(options?: Options);
|
|
24
|
+
get_by_id: (id: Path_Id) => Source_File | undefined;
|
|
25
|
+
get_or_create: (id: Path_Id) => Source_File;
|
|
26
|
+
watch(listener: On_Filer_Change): Promise<Cleanup_Watch>;
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=filer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filer.d.ts","sourceRoot":"../src/lib/","sources":["filer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AAGzD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,EACN,SAAS,EAET,KAAK,cAAc,EACnB,KAAK,OAAO,IAAI,iBAAiB,EAEjC,MAAM,gBAAgB,CAAC;AAUxB,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACtC,YAAY,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;AAEzF,MAAM,WAAW,OAAO;IACvB,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC;CACzE;AAED,qBAAa,KAAK;;IACjB,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAa;gBAKjC,OAAO,GAAE,OAAsB;IAU3C,SAAS,OAAQ,OAAO,KAAG,WAAW,GAAG,SAAS,CAEhD;IAEF,aAAa,OAAQ,OAAO,KAAG,WAAW,CAWxC;IA4HI,KAAK,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAKxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ5B"}
|
package/dist/filer.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { EMPTY_OBJECT } from '@ryanatkn/belt/object.js';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { wait } from '@ryanatkn/belt/async.js';
|
|
5
|
+
import { watch_dir, } from './watch_dir.js';
|
|
6
|
+
import { default_file_filter, paths } from './paths.js';
|
|
7
|
+
import { parse_imports } from './parse_imports.js';
|
|
8
|
+
import { resolve_specifier } from './resolve_specifier.js';
|
|
9
|
+
import { default_sveltekit_config } from './sveltekit_config.js';
|
|
10
|
+
import { map_sveltekit_aliases } from './sveltekit_helpers.js';
|
|
11
|
+
import { Unreachable_Error } from '@ryanatkn/belt/error.js';
|
|
12
|
+
const aliases = Object.entries(default_sveltekit_config.alias);
|
|
13
|
+
export class Filer {
|
|
14
|
+
files = new Map();
|
|
15
|
+
#watch_dir;
|
|
16
|
+
#watch_dir_options;
|
|
17
|
+
constructor(options = EMPTY_OBJECT) {
|
|
18
|
+
this.#watch_dir = options.watch_dir ?? watch_dir;
|
|
19
|
+
this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
|
|
20
|
+
}
|
|
21
|
+
#watching;
|
|
22
|
+
#listeners = new Set();
|
|
23
|
+
#ready = false;
|
|
24
|
+
get_by_id = (id) => {
|
|
25
|
+
return this.files.get(id);
|
|
26
|
+
};
|
|
27
|
+
get_or_create = (id) => {
|
|
28
|
+
const existing = this.get_by_id(id);
|
|
29
|
+
if (existing)
|
|
30
|
+
return existing;
|
|
31
|
+
const file = {
|
|
32
|
+
id,
|
|
33
|
+
contents: null,
|
|
34
|
+
dependents: new Map(),
|
|
35
|
+
dependencies: new Map(),
|
|
36
|
+
};
|
|
37
|
+
this.files.set(id, file);
|
|
38
|
+
return file;
|
|
39
|
+
};
|
|
40
|
+
#update(id) {
|
|
41
|
+
const file = this.get_or_create(id);
|
|
42
|
+
const new_contents = existsSync(id) ? readFileSync(id, 'utf8') : null;
|
|
43
|
+
const contents_changed = file.contents !== new_contents;
|
|
44
|
+
file.contents = new_contents;
|
|
45
|
+
if (contents_changed) {
|
|
46
|
+
const dir = dirname(file.id);
|
|
47
|
+
const dependencies_before = new Set(file.dependencies.keys());
|
|
48
|
+
const dependencies_removed = new Set(dependencies_before);
|
|
49
|
+
const imported = file.contents ? parse_imports(file.id, file.contents) : [];
|
|
50
|
+
for (const specifier of imported) {
|
|
51
|
+
// TODO logic is duplicated from loader
|
|
52
|
+
const path = map_sveltekit_aliases(specifier, aliases);
|
|
53
|
+
// The specifier `path` has now been mapped to its final form, so we can inspect it.
|
|
54
|
+
if (path[0] === '.' || path[0] === '/') {
|
|
55
|
+
const { path_id } = resolve_specifier(path, dir);
|
|
56
|
+
dependencies_removed.delete(path_id);
|
|
57
|
+
if (!dependencies_before.has(path_id)) {
|
|
58
|
+
const d = this.get_or_create(path_id);
|
|
59
|
+
file.dependencies.set(d.id, d);
|
|
60
|
+
d.dependents.set(file.id, file);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// update any removed dependencies
|
|
65
|
+
for (const dependency_removed of dependencies_removed) {
|
|
66
|
+
const deleted1 = file.dependencies.delete(dependency_removed);
|
|
67
|
+
if (!deleted1)
|
|
68
|
+
throw Error('expected to delete1 ' + file.id); // TODO @many delete if correct
|
|
69
|
+
const dependency_removed_file = this.get_or_create(dependency_removed);
|
|
70
|
+
const deleted2 = dependency_removed_file.dependents.delete(file.id);
|
|
71
|
+
if (!deleted2)
|
|
72
|
+
throw Error('expected to delete2 ' + file.id); // TODO @many delete if correct
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return file;
|
|
76
|
+
}
|
|
77
|
+
#remove(id) {
|
|
78
|
+
const file = this.get_by_id(id);
|
|
79
|
+
if (!file)
|
|
80
|
+
return; // this is safe because the object would exist if any other file referenced it as a dependency or dependent
|
|
81
|
+
file.contents = null; // clear contents in case it gets re-added later, we want the change to be detected
|
|
82
|
+
let found = false;
|
|
83
|
+
for (const d of this.files.values()) {
|
|
84
|
+
if (d.dependencies.has(file.id)) {
|
|
85
|
+
found = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!found)
|
|
90
|
+
this.files.delete(id);
|
|
91
|
+
return file;
|
|
92
|
+
}
|
|
93
|
+
#notify_listener(listener) {
|
|
94
|
+
if (!this.#ready)
|
|
95
|
+
return;
|
|
96
|
+
for (const source_file of this.files.values()) {
|
|
97
|
+
listener({ type: 'add', path: source_file.id, is_directory: false }, source_file);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
#notify_change(change, source_file) {
|
|
101
|
+
if (!this.#ready)
|
|
102
|
+
return;
|
|
103
|
+
for (const listener of this.#listeners) {
|
|
104
|
+
listener(change, source_file);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async #add_listener(listener) {
|
|
108
|
+
this.#listeners.add(listener);
|
|
109
|
+
if (this.#watching) {
|
|
110
|
+
// if already watching, call the listener for all existing files after init
|
|
111
|
+
await this.#watching.init();
|
|
112
|
+
await wait(); // wait a tick to ensure the `this.#ready` value is updated below first
|
|
113
|
+
this.#notify_listener(listener);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.#watching = this.#watch_dir({
|
|
117
|
+
filter: (path, is_directory) => (is_directory ? true : default_file_filter(path)),
|
|
118
|
+
...this.#watch_dir_options,
|
|
119
|
+
dir: resolve(this.#watch_dir_options.dir ?? paths.source),
|
|
120
|
+
on_change: this.#on_change,
|
|
121
|
+
});
|
|
122
|
+
await this.#watching.init();
|
|
123
|
+
this.#ready = true;
|
|
124
|
+
this.#notify_listener(listener);
|
|
125
|
+
}
|
|
126
|
+
async #remove_listener(listener) {
|
|
127
|
+
this.#listeners.delete(listener);
|
|
128
|
+
if (this.#listeners.size === 0) {
|
|
129
|
+
await this.close(); // TODO is this right? should `watch` be async?
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
#on_change = (change) => {
|
|
133
|
+
if (change.is_directory)
|
|
134
|
+
return;
|
|
135
|
+
let source_file;
|
|
136
|
+
switch (change.type) {
|
|
137
|
+
case 'add':
|
|
138
|
+
case 'update': {
|
|
139
|
+
source_file = this.#update(change.path);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'delete': {
|
|
143
|
+
source_file = this.#remove(change.path);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
default:
|
|
147
|
+
throw new Unreachable_Error(change.type);
|
|
148
|
+
}
|
|
149
|
+
if (source_file) {
|
|
150
|
+
this.#notify_change(change, source_file);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
async watch(listener) {
|
|
154
|
+
await this.#add_listener(listener);
|
|
155
|
+
return () => this.#remove_listener(listener);
|
|
156
|
+
}
|
|
157
|
+
async close() {
|
|
158
|
+
this.#ready = false;
|
|
159
|
+
this.#listeners.clear();
|
|
160
|
+
if (this.#watching) {
|
|
161
|
+
await this.#watching.close();
|
|
162
|
+
this.#watching = undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
package/dist/gen.task.d.ts
CHANGED
|
@@ -6,12 +6,12 @@ export declare const Args: z.ZodObject<{
|
|
|
6
6
|
check: z.ZodDefault<z.ZodBoolean>;
|
|
7
7
|
}, "strict", z.ZodTypeAny, {
|
|
8
8
|
_: string[];
|
|
9
|
-
check: boolean;
|
|
10
9
|
root_dirs: string[];
|
|
10
|
+
check: boolean;
|
|
11
11
|
}, {
|
|
12
12
|
_?: string[] | undefined;
|
|
13
|
-
check?: boolean | undefined;
|
|
14
13
|
root_dirs?: string[] | undefined;
|
|
14
|
+
check?: boolean | undefined;
|
|
15
15
|
}>;
|
|
16
16
|
export type Args = z.infer<typeof Args>;
|
|
17
17
|
export declare const task: Task<Args>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gro.config.default.d.ts","sourceRoot":"../src/lib/","sources":["gro.config.default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"gro.config.default.d.ts","sourceRoot":"../src/lib/","sources":["gro.config.default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,iBAAiB,CAAC;AAOvD;;;;;;;;GAQG;AACH,QAAA,MAAM,MAAM,EAAE,iBAcb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -2,6 +2,7 @@ import { gro_plugin_sveltekit_library } from './gro_plugin_sveltekit_library.js'
|
|
|
2
2
|
import { has_server, gro_plugin_server } from './gro_plugin_server.js';
|
|
3
3
|
import { gro_plugin_sveltekit_app } from './gro_plugin_sveltekit_app.js';
|
|
4
4
|
import { has_sveltekit_app, has_sveltekit_library } from './sveltekit_helpers.js';
|
|
5
|
+
import { gro_plugin_gen } from './gro_plugin_gen.js';
|
|
5
6
|
/**
|
|
6
7
|
* This is the default config that's passed to `gro.config.ts`
|
|
7
8
|
* if it exists in the current project, and if not, this is the final config.
|
|
@@ -19,9 +20,7 @@ const config = async (cfg) => {
|
|
|
19
20
|
has_sveltekit_app_result.ok
|
|
20
21
|
? gro_plugin_sveltekit_app({ host_target: has_server_result.ok ? 'node' : 'github_pages' })
|
|
21
22
|
: null,
|
|
22
|
-
|
|
23
|
-
// import {gro_plugin_gen} from './gro_plugin_gen.js';
|
|
24
|
-
// gro_plugin_gen(),
|
|
23
|
+
gro_plugin_gen(),
|
|
25
24
|
];
|
|
26
25
|
return cfg;
|
|
27
26
|
};
|
package/dist/gro_plugin_gen.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import type { Plugin
|
|
1
|
+
import type { Plugin } from './plugin.js';
|
|
2
2
|
import type { Args } from './args.js';
|
|
3
|
+
import type { File_Filter, Path_Id } from './path.js';
|
|
4
|
+
import type { Source_File } from './filer.js';
|
|
3
5
|
export interface Task_Args extends Args {
|
|
4
6
|
watch?: boolean;
|
|
5
7
|
}
|
|
6
|
-
export
|
|
8
|
+
export interface Options {
|
|
9
|
+
root_dirs?: string[];
|
|
10
|
+
flush_debounce_delay?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const gro_plugin_gen: ({ root_dirs, flush_debounce_delay, }?: Options) => Plugin;
|
|
13
|
+
export declare const filter_dependents: (source_file: Source_File, get_by_id: (id: Path_Id) => Source_File | undefined, filter?: File_Filter, results?: Set<string>, searched?: Set<string>) => Set<string>;
|
|
7
14
|
//# sourceMappingURL=gro_plugin_gen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gro_plugin_gen.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_gen.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gro_plugin_gen.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_gen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAKpC,OAAO,KAAK,EAAC,WAAW,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAgB,WAAW,EAAC,MAAM,YAAY,CAAC;AAK3D,MAAM,WAAW,SAAU,SAAQ,IAAI;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,cAAc,0CAGxB,OAAO,KAAkB,MA+F3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,gBAChB,WAAW,aACb,CAAC,EAAE,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,WAC1C,WAAW,YACX,GAAG,CAAC,MAAM,CAAC,aACV,GAAG,CAAC,MAAM,CAAC,KACnB,GAAG,CAAC,MAAM,CAYZ,CAAC"}
|
package/dist/gro_plugin_gen.js
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// @ts-nocheck
|
|
4
|
-
import { path_id_to_base_path } from './paths.js';
|
|
1
|
+
import { EMPTY_OBJECT } from '@ryanatkn/belt/object.js';
|
|
2
|
+
import { paths } from './paths.js';
|
|
5
3
|
import { find_genfiles, is_gen_path } from './gen.js';
|
|
6
|
-
import { filter_dependents } from './build/source_file.js';
|
|
7
4
|
import { throttle } from './throttle.js';
|
|
5
|
+
import { spawn_cli } from './cli.js';
|
|
6
|
+
import { Unreachable_Error } from '@ryanatkn/belt/error.js';
|
|
8
7
|
const FLUSH_DEBOUNCE_DELAY = 500;
|
|
9
|
-
export const
|
|
8
|
+
export const gro_plugin_gen = ({ root_dirs = [paths.source], flush_debounce_delay = FLUSH_DEBOUNCE_DELAY, } = EMPTY_OBJECT) => {
|
|
9
|
+
const input_path = paths.source; // TODO option?
|
|
10
10
|
let generating = false;
|
|
11
11
|
let regen = false;
|
|
12
|
-
let
|
|
12
|
+
let flushing_timeout;
|
|
13
13
|
const queued_files = new Set();
|
|
14
|
-
const queue_gen = (
|
|
15
|
-
queued_files.add(
|
|
16
|
-
|
|
14
|
+
const queue_gen = (gen_file_id) => {
|
|
15
|
+
queued_files.add(gen_file_id);
|
|
16
|
+
if (flushing_timeout === undefined) {
|
|
17
|
+
flushing_timeout = setTimeout(() => {
|
|
18
|
+
flushing_timeout = undefined;
|
|
19
|
+
void flush_gen_queue();
|
|
20
|
+
}); // the timeout batches synchronously
|
|
21
|
+
}
|
|
17
22
|
};
|
|
18
23
|
const flush_gen_queue = throttle(async () => {
|
|
19
24
|
// hacky way to avoid concurrent `gro gen` calls
|
|
@@ -30,50 +35,75 @@ export const plugin = () => {
|
|
|
30
35
|
regen = false;
|
|
31
36
|
void flush_gen_queue();
|
|
32
37
|
}
|
|
33
|
-
},
|
|
38
|
+
}, flush_debounce_delay);
|
|
34
39
|
const gen = (files = []) => spawn_cli('gro', ['gen', ...files]);
|
|
40
|
+
let cleanup;
|
|
35
41
|
return {
|
|
36
42
|
name: 'gro_plugin_gen',
|
|
37
|
-
setup: async ({
|
|
43
|
+
setup: async ({ watch, dev, log, config, filer }) => {
|
|
38
44
|
// For production builds, we assume `gen` is already fresh,
|
|
39
45
|
// which should be checked by CI via `gro check` which calls `gro gen --check`.
|
|
40
46
|
if (!dev)
|
|
41
47
|
return;
|
|
42
|
-
// Run `gen`, first checking if there are any modules to avoid a console error.
|
|
43
|
-
// Some parts of the build may have already happened,
|
|
44
|
-
// making us miss `build` events for gen dependencies,
|
|
45
|
-
// so we run `gen` here even if it's usually wasteful.
|
|
46
|
-
const found = find_genfiles([paths.source], root_dirs, config);
|
|
47
|
-
if (found.ok && found.value.resolved_input_files.size > 0) {
|
|
48
|
-
await gen();
|
|
49
|
-
}
|
|
50
48
|
// Do we need to just generate everything once and exit?
|
|
51
49
|
// TODO could we have an esbuild context here? problem is watching the right files, maybe a plugin that tracks deps
|
|
52
|
-
if (!
|
|
50
|
+
if (!watch) {
|
|
53
51
|
log.info('generating and exiting early');
|
|
52
|
+
// Run `gen`, first checking if there are any modules to avoid a console error.
|
|
53
|
+
// Some parts of the build may have already happened,
|
|
54
|
+
// making us miss `build` events for gen dependencies,
|
|
55
|
+
// so we run `gen` here even if it's usually wasteful.
|
|
56
|
+
const found = find_genfiles([input_path], root_dirs, config);
|
|
57
|
+
if (found.ok && found.value.resolved_input_files.length > 0) {
|
|
58
|
+
await gen();
|
|
59
|
+
}
|
|
54
60
|
return;
|
|
55
61
|
}
|
|
56
62
|
// When a file builds, check it and its tree of dependents
|
|
57
63
|
// for any `.gen.` files that need to run.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
cleanup = await filer.watch((change, source_file) => {
|
|
65
|
+
switch (change.type) {
|
|
66
|
+
case 'add':
|
|
67
|
+
case 'update': {
|
|
68
|
+
// TODO how to handle this now? the loader traces deps for us with `parentPath`,
|
|
69
|
+
// but we probably want to make this an esbuild plugin instead
|
|
70
|
+
if (is_gen_path(source_file.id)) {
|
|
71
|
+
queue_gen(source_file.id);
|
|
72
|
+
}
|
|
73
|
+
const dependent_gen_file_ids = filter_dependents(source_file, filer.get_by_id, is_gen_path);
|
|
74
|
+
for (const dependent_gen_file_id of dependent_gen_file_ids) {
|
|
75
|
+
queue_gen(dependent_gen_file_id);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case 'delete': {
|
|
80
|
+
// TODO delete the generated file(s)? option?
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
default:
|
|
84
|
+
throw new Unreachable_Error(change.type);
|
|
64
85
|
}
|
|
65
|
-
|
|
66
|
-
is_gen_path);
|
|
67
|
-
for (const dependent_gen_file_id of dependent_gen_file_ids) {
|
|
68
|
-
queue_gen(path_id_to_base_path(dependent_gen_file_id));
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
filer.on('build', on_filer_build);
|
|
86
|
+
});
|
|
72
87
|
},
|
|
73
|
-
teardown: (
|
|
74
|
-
if (
|
|
75
|
-
|
|
88
|
+
teardown: async () => {
|
|
89
|
+
if (cleanup !== undefined) {
|
|
90
|
+
await cleanup();
|
|
91
|
+
cleanup = undefined;
|
|
76
92
|
}
|
|
77
93
|
},
|
|
78
94
|
};
|
|
79
95
|
};
|
|
96
|
+
export const filter_dependents = (source_file, get_by_id, filter, results = new Set(), searched = new Set()) => {
|
|
97
|
+
const { dependents } = source_file;
|
|
98
|
+
for (const dependent_id of dependents.keys()) {
|
|
99
|
+
if (searched.has(dependent_id))
|
|
100
|
+
continue;
|
|
101
|
+
searched.add(dependent_id);
|
|
102
|
+
if (!filter || filter(dependent_id)) {
|
|
103
|
+
results.add(dependent_id);
|
|
104
|
+
}
|
|
105
|
+
const dependent_source_File = get_by_id(dependent_id);
|
|
106
|
+
filter_dependents(dependent_source_File, get_by_id, filter, results, searched);
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gro_plugin_sveltekit_library.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_sveltekit_library.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"gro_plugin_sveltekit_library.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_sveltekit_library.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAGxC,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,OAAO;IACvB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,4BAA4B,qDAGtC,OAAO,KAAQ,MA8BjB,CAAC"}
|
|
@@ -1,26 +1,14 @@
|
|
|
1
1
|
import { print_spawn_result, spawn } from '@ryanatkn/belt/process.js';
|
|
2
2
|
import { Task_Error } from './task.js';
|
|
3
3
|
import { load_package_json } from './package_json.js';
|
|
4
|
-
import {
|
|
5
|
-
import { find_cli, spawn_cli } from './cli.js';
|
|
6
|
-
import { SVELTE_PACKAGE_CLI, has_sveltekit_library, } from './sveltekit_helpers.js';
|
|
4
|
+
import { SVELTE_PACKAGE_CLI, run_svelte_package, } from './sveltekit_helpers.js';
|
|
7
5
|
export const gro_plugin_sveltekit_library = ({ svelte_package_options, svelte_package_cli = SVELTE_PACKAGE_CLI, } = {}) => {
|
|
8
6
|
return {
|
|
9
7
|
name: 'gro_plugin_sveltekit_library',
|
|
10
|
-
setup: async ({ log }) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
throw new Task_Error('Failed to find SvelteKit library: ' + has_sveltekit_library_result.message);
|
|
8
|
+
setup: async ({ dev, log }) => {
|
|
9
|
+
if (!dev) {
|
|
10
|
+
await run_svelte_package(svelte_package_options, svelte_package_cli, log);
|
|
14
11
|
}
|
|
15
|
-
const found_svelte_package_cli = find_cli(svelte_package_cli);
|
|
16
|
-
if (found_svelte_package_cli?.kind !== 'local') {
|
|
17
|
-
throw new Task_Error(`Failed to find SvelteKit packaging CLI \`${svelte_package_cli}\`, do you need to run \`npm i\`?`);
|
|
18
|
-
}
|
|
19
|
-
const serialized_args = serialize_args({
|
|
20
|
-
...svelte_package_options,
|
|
21
|
-
...to_forwarded_args(svelte_package_cli),
|
|
22
|
-
});
|
|
23
|
-
await spawn_cli(found_svelte_package_cli, serialized_args, log);
|
|
24
12
|
},
|
|
25
13
|
adapt: async ({ log, timings }) => {
|
|
26
14
|
const package_json = load_package_json();
|
package/dist/invoke_task.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Timings } from '@ryanatkn/belt/timings.js';
|
|
|
2
2
|
import { type Args } from './args.js';
|
|
3
3
|
import { Raw_Input_Path } from './input_path.js';
|
|
4
4
|
import type { Gro_Config } from './gro_config.js';
|
|
5
|
+
import { Filer } from './filer.js';
|
|
5
6
|
/**
|
|
6
7
|
* Invokes Gro tasks by name using the filesystem as the source.
|
|
7
8
|
*
|
|
@@ -24,5 +25,5 @@ import type { Gro_Config } from './gro_config.js';
|
|
|
24
25
|
* @param config - The Gro configuration.
|
|
25
26
|
* @param initial_timings - The timings to use for the top-level task, `null` for composed tasks.
|
|
26
27
|
*/
|
|
27
|
-
export declare const invoke_task: (task_name: Raw_Input_Path, args: Args | undefined, config: Gro_Config, initial_timings?: Timings | null) => Promise<void>;
|
|
28
|
+
export declare const invoke_task: (task_name: Raw_Input_Path, args: Args | undefined, config: Gro_Config, initial_filer?: Filer, initial_timings?: Timings | null) => Promise<void>;
|
|
28
29
|
//# sourceMappingURL=invoke_task.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke_task.d.ts","sourceRoot":"../src/lib/","sources":["invoke_task.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGpE,OAAO,EAAoB,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAgB,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAI9D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"invoke_task.d.ts","sourceRoot":"../src/lib/","sources":["invoke_task.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGpE,OAAO,EAAoB,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAgB,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAI9D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,cACZ,cAAc,QACnB,IAAI,GAAG,SAAS,UACd,UAAU,kBACF,KAAK,oBACH,OAAO,GAAG,IAAI,KAC9B,OAAO,CAAC,IAAI,CAgFd,CAAC"}
|
package/dist/invoke_task.js
CHANGED
|
@@ -8,6 +8,7 @@ import { to_input_path, Raw_Input_Path } from './input_path.js';
|
|
|
8
8
|
import { find_tasks, load_tasks, Silent_Error } from './task.js';
|
|
9
9
|
import { load_gro_package_json } from './package_json.js';
|
|
10
10
|
import { log_tasks, log_error_reasons } from './task_logging.js';
|
|
11
|
+
import { Filer } from './filer.js';
|
|
11
12
|
/**
|
|
12
13
|
* Invokes Gro tasks by name using the filesystem as the source.
|
|
13
14
|
*
|
|
@@ -30,9 +31,10 @@ import { log_tasks, log_error_reasons } from './task_logging.js';
|
|
|
30
31
|
* @param config - The Gro configuration.
|
|
31
32
|
* @param initial_timings - The timings to use for the top-level task, `null` for composed tasks.
|
|
32
33
|
*/
|
|
33
|
-
export const invoke_task = async (task_name, args, config, initial_timings
|
|
34
|
+
export const invoke_task = async (task_name, args, config, initial_filer, initial_timings) => {
|
|
34
35
|
const log = new System_Logger(print_log_label(task_name || 'gro'));
|
|
35
36
|
log.info('invoking', task_name ? st('cyan', task_name) : 'gro');
|
|
37
|
+
const filer = initial_filer ?? new Filer();
|
|
36
38
|
const timings = initial_timings ?? new Timings();
|
|
37
39
|
const total_timing = create_stopwatch();
|
|
38
40
|
const finish = () => {
|
|
@@ -80,7 +82,7 @@ export const invoke_task = async (task_name, args, config, initial_timings = nul
|
|
|
80
82
|
const task = loaded_tasks.modules[0];
|
|
81
83
|
log.info(`→ ${st('cyan', task.name)} ${(task.mod.task.summary && st('gray', task.mod.task.summary)) ?? ''}`);
|
|
82
84
|
const timing_to_run_task = timings.start('run task ' + task_name);
|
|
83
|
-
const result = await run_task(task, { ...args, ...to_forwarded_args(`gro ${task.name}`) }, invoke_task, config, timings);
|
|
85
|
+
const result = await run_task(task, { ...args, ...to_forwarded_args(`gro ${task.name}`) }, invoke_task, config, filer, timings);
|
|
84
86
|
timing_to_run_task();
|
|
85
87
|
if (!result.ok) {
|
|
86
88
|
log.info(`${st('red', '🞩')} ${st('cyan', task.name)}`);
|
package/dist/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"../src/lib/","sources":["loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,QAAQ,EAAE,WAAW,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"../src/lib/","sources":["loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,QAAQ,EAAE,WAAW,EAAC,MAAM,aAAa,CAAC;AAgFvD,eAAO,MAAM,IAAI,EAAE,QAwFlB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAoDrB,CAAC"}
|
package/dist/loader.js
CHANGED
|
@@ -13,6 +13,7 @@ import { JSON_MATCHER, NODE_MODULES_DIRNAME, TS_MATCHER } from './path_constants
|
|
|
13
13
|
import { to_define_import_meta_env, default_ts_transform_options } from './esbuild_helpers.js';
|
|
14
14
|
import { resolve_specifier } from './resolve_specifier.js';
|
|
15
15
|
import { resolve_node_specifier } from './resolve_node_specifier.js';
|
|
16
|
+
import { map_sveltekit_aliases } from './sveltekit_helpers.js';
|
|
16
17
|
/*
|
|
17
18
|
|
|
18
19
|
Usage via `$lib/register.ts`:
|
|
@@ -47,7 +48,7 @@ const ts_transform_options = {
|
|
|
47
48
|
define: to_define_import_meta_env(dev, base_url),
|
|
48
49
|
sourcemap: 'inline',
|
|
49
50
|
};
|
|
50
|
-
const aliases = Object.entries(
|
|
51
|
+
const aliases = Object.entries(alias);
|
|
51
52
|
const RAW_MATCHER = /(%3Fraw|\.css|\.svg)$/; // TODO others? configurable?
|
|
52
53
|
const ENV_MATCHER = /src\/lib\/\$env\/(static|dynamic)\/(public|private)$/;
|
|
53
54
|
const NODE_MODULES_MATCHER = new RegExp(escape_regexp('/' + NODE_MODULES_DIRNAME + '/'), 'u');
|
|
@@ -151,14 +152,7 @@ export const resolve = async (specifier, context, nextResolve) => {
|
|
|
151
152
|
if (shimmed !== undefined) {
|
|
152
153
|
return nextResolve(shimmed, context);
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
-
// Map the path with the SvelteKit aliases.
|
|
156
|
-
for (const [from, to] of aliases) {
|
|
157
|
-
if (path.startsWith(from)) {
|
|
158
|
-
path = join(dir, to, path.substring(from.length));
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
155
|
+
const path = map_sveltekit_aliases(specifier, aliases);
|
|
162
156
|
// The specifier `path` has now been mapped to its final form, so we can inspect it.
|
|
163
157
|
if (path[0] !== '.' && path[0] !== '/') {
|
|
164
158
|
// Resolve to `node_modules`.
|
package/dist/package.d.ts
CHANGED
|
@@ -177,6 +177,10 @@ export declare const package_json: {
|
|
|
177
177
|
types: string;
|
|
178
178
|
default: string;
|
|
179
179
|
};
|
|
180
|
+
'./filer.js': {
|
|
181
|
+
types: string;
|
|
182
|
+
default: string;
|
|
183
|
+
};
|
|
180
184
|
'./format_directory.js': {
|
|
181
185
|
types: string;
|
|
182
186
|
default: string;
|
|
@@ -289,6 +293,10 @@ export declare const package_json: {
|
|
|
289
293
|
types: string;
|
|
290
294
|
default: string;
|
|
291
295
|
};
|
|
296
|
+
'./parse_imports.js': {
|
|
297
|
+
types: string;
|
|
298
|
+
default: string;
|
|
299
|
+
};
|
|
292
300
|
'./path_constants.js': {
|
|
293
301
|
types: string;
|
|
294
302
|
default: string;
|
|
@@ -582,6 +590,13 @@ export declare const src_json: {
|
|
|
582
590
|
kind: string;
|
|
583
591
|
}[];
|
|
584
592
|
};
|
|
593
|
+
'./filer.js': {
|
|
594
|
+
path: string;
|
|
595
|
+
declarations: {
|
|
596
|
+
name: string;
|
|
597
|
+
kind: string;
|
|
598
|
+
}[];
|
|
599
|
+
};
|
|
585
600
|
'./format_directory.js': {
|
|
586
601
|
path: string;
|
|
587
602
|
declarations: {
|
|
@@ -772,6 +787,13 @@ export declare const src_json: {
|
|
|
772
787
|
kind: string;
|
|
773
788
|
}[];
|
|
774
789
|
};
|
|
790
|
+
'./parse_imports.js': {
|
|
791
|
+
path: string;
|
|
792
|
+
declarations: {
|
|
793
|
+
name: string;
|
|
794
|
+
kind: string;
|
|
795
|
+
}[];
|
|
796
|
+
};
|
|
775
797
|
'./path_constants.js': {
|
|
776
798
|
path: string;
|
|
777
799
|
declarations: {
|
package/dist/package.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.d.ts","sourceRoot":"../src/lib/","sources":["package.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"package.d.ts","sourceRoot":"../src/lib/","sources":["package.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuQD,CAAC;AAEzB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8vBD,CAAC"}
|