eddev 2.0.0-beta.52 → 2.0.0-beta.54
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/app/lib/routing/types.d.ts +8 -2
- package/dist/node/cli/cli-worker.js +5 -1
- package/dist/node/cli/cli.js +8 -2
- package/dist/node/cli/display/CLIApp.js +1 -1
- package/dist/node/cli/version.d.ts +1 -1
- package/dist/node/cli/version.js +1 -1
- package/dist/node/graphql/graphql-codegen.d.ts +10 -1
- package/dist/node/graphql/graphql-codegen.js +119 -30
- package/dist/node/graphql/plugins/gql-plugin-queries.js +1 -1
- package/dist/node/graphql/query-files-loader.d.ts +3 -0
- package/dist/node/graphql/query-files-loader.js +5 -0
- package/dist/node/project/manifest/manifest.d.ts +1 -0
- package/dist/node/project/manifest/manifest.js +14 -10
- package/dist/node/utils/fs-codegen.d.ts +2 -0
- package/dist/node/utils/fs-codegen.js +2 -1
- package/dist/node/utils/watch-file-tree.d.ts +17 -3
- package/dist/node/utils/watch-file-tree.js +12 -5
- package/package.json +1 -1
|
@@ -109,7 +109,13 @@ export type RouterAPI = {
|
|
|
109
109
|
replaceQuery: (search: Record<string, string | string[]>) => void;
|
|
110
110
|
/** Replace the hash */
|
|
111
111
|
replaceHash: (hash: string) => void;
|
|
112
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Handle a click event, potentially triggering a route change.
|
|
114
|
+
*
|
|
115
|
+
* @param e A pointer or mouse event.
|
|
116
|
+
* @param href An optional URL. This must be provided if no `href` property exists on the clicked element
|
|
117
|
+
* @param preferBack If set, clicking this link will send the user 'back', when clicking a link to the previous history item.
|
|
118
|
+
*/
|
|
113
119
|
handleClickEvent(e: PointerOrMouseEvent, href?: string, preferBack?: boolean | "exact"): void;
|
|
114
120
|
/** A reference to the route loader (mostly for internal use) */
|
|
115
121
|
loader: RouteLoader;
|
|
@@ -117,7 +123,7 @@ export type RouterAPI = {
|
|
|
117
123
|
subscribe: (subscribe: RouterSubscriber) => () => void;
|
|
118
124
|
/** This function is used by the routing system automatically */
|
|
119
125
|
emitEvent: (event: RouterEvent) => void;
|
|
120
|
-
/** Returns the current
|
|
126
|
+
/** Returns the current RouterAPIState */
|
|
121
127
|
getState: () => RouterAPIState | null;
|
|
122
128
|
/** Go back to a previous route in the history stack, or push it onto the stack if it isn't currently on the stack. */
|
|
123
129
|
restoreRoute: (route: RouteState) => void;
|
|
@@ -106,7 +106,11 @@ if (!isMainThread) {
|
|
|
106
106
|
pipeLog(frontendLog);
|
|
107
107
|
}
|
|
108
108
|
else if (mode === "graphql") {
|
|
109
|
-
const codegen = new GraphQLGenerator(project
|
|
109
|
+
const codegen = new GraphQLGenerator(project, {
|
|
110
|
+
generate: true,
|
|
111
|
+
optimize: true,
|
|
112
|
+
watch: true,
|
|
113
|
+
});
|
|
110
114
|
codegen.start();
|
|
111
115
|
pipeLog(graphqlLog);
|
|
112
116
|
}
|
package/dist/node/cli/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { AdminBundler, adminLog } from "../compiler/bundler.admin.js";
|
|
|
6
6
|
import { FrontendBundler, frontendLog } from "../compiler/bundler.frontend.js";
|
|
7
7
|
import { DevServer, serverlessLog } from "../compiler/dev-server.js";
|
|
8
8
|
import { createVinxiCodegen } from "../compiler/vinxi-codegen.js";
|
|
9
|
-
import { graphqlLog } from "../graphql/graphql-codegen.js";
|
|
9
|
+
import { GraphQLGenerator, graphqlLog } from "../graphql/graphql-codegen.js";
|
|
10
10
|
import { BuildInfoWriter } from "../project/eddev-build-file.js";
|
|
11
11
|
import { Project, projectLog } from "../project/project.js";
|
|
12
12
|
import { describeBlockManifest } from "../types/block-type.js";
|
|
@@ -143,6 +143,9 @@ program
|
|
|
143
143
|
.option("-s, --serverless", 'Build in "serverless" mode', false)
|
|
144
144
|
.option("--verbose", "Show extra debugging info", false)
|
|
145
145
|
.action(async (options) => {
|
|
146
|
+
process.env["NODE_ENV"] = "production";
|
|
147
|
+
/** Ignore self-signed certificate errors in dev mode */
|
|
148
|
+
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
|
|
146
149
|
init(options.verbose);
|
|
147
150
|
configureCliMode({
|
|
148
151
|
interactive: false,
|
|
@@ -181,7 +184,10 @@ program
|
|
|
181
184
|
const frontend = new FrontendBundler(project, {
|
|
182
185
|
mode: "production",
|
|
183
186
|
});
|
|
184
|
-
|
|
187
|
+
const graphql = new GraphQLGenerator(project, {
|
|
188
|
+
optimize: true,
|
|
189
|
+
});
|
|
190
|
+
await Promise.all([admin.start(), frontend.start(), graphql.start()]);
|
|
185
191
|
console.log("Done building SPA WordPress");
|
|
186
192
|
}
|
|
187
193
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "2.0.0-beta.
|
|
1
|
+
export declare const VERSION = "2.0.0-beta.54";
|
package/dist/node/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "2.0.0-beta.
|
|
1
|
+
export const VERSION = "2.0.0-beta.54";
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { Project } from "../project/project.js";
|
|
2
2
|
export declare const graphqlLog: import("../utils/stateful-log.js").StatefulLog<GraphQLGenerator>;
|
|
3
|
+
type GraphQLGeneratorOptions = {
|
|
4
|
+
watch?: boolean;
|
|
5
|
+
optimize?: boolean;
|
|
6
|
+
generate?: boolean;
|
|
7
|
+
};
|
|
3
8
|
export declare class GraphQLGenerator {
|
|
4
9
|
project: Project;
|
|
10
|
+
opts: GraphQLGeneratorOptions;
|
|
5
11
|
private needsRegenerate;
|
|
6
12
|
private needsReload;
|
|
7
13
|
private regenerateReason;
|
|
@@ -9,14 +15,16 @@ export declare class GraphQLGenerator {
|
|
|
9
15
|
private isGenerating;
|
|
10
16
|
private schemaLoader;
|
|
11
17
|
private fileLoader;
|
|
18
|
+
private optimizeWriter;
|
|
12
19
|
private debouncer?;
|
|
13
20
|
private retryTimer?;
|
|
14
|
-
constructor(project: Project);
|
|
21
|
+
constructor(project: Project, opts: GraphQLGeneratorOptions);
|
|
15
22
|
queueRegenerate(full: boolean, reason: string, info?: string | string[]): Promise<void>;
|
|
16
23
|
start(): Promise<void>;
|
|
17
24
|
private listen;
|
|
18
25
|
scheduleRetry(): void;
|
|
19
26
|
generate(): Promise<void>;
|
|
27
|
+
private getOptimizedQueries;
|
|
20
28
|
/**
|
|
21
29
|
* Validates a GraphQL manifest against a schema, ensuring there are no user errors.
|
|
22
30
|
*/
|
|
@@ -28,3 +36,4 @@ export declare class GraphQLGenerator {
|
|
|
28
36
|
*/
|
|
29
37
|
private writeSchemaFile;
|
|
30
38
|
}
|
|
39
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { codegen } from "@graphql-codegen/core";
|
|
2
|
-
import { Kind, visit } from "graphql";
|
|
2
|
+
import { Kind, visit, print, } from "graphql";
|
|
3
3
|
import { NoUnusedFragmentsRule, specifiedRules, validate } from "graphql/validation/index.js";
|
|
4
4
|
import { join, relative, resolve } from "path";
|
|
5
5
|
import { ProjectEnvUtils } from "../project/env.js";
|
|
@@ -16,6 +16,7 @@ import { watchFileTreeForChanges } from "../utils/watch-file-tree.js";
|
|
|
16
16
|
import pluginFiles from "./plugins/gql-plugin-files.js";
|
|
17
17
|
import pluginNoDuplicates from "./plugins/gql-plugin-no-duplicates.js";
|
|
18
18
|
import pluginQueries from "./plugins/gql-plugin-queries.js";
|
|
19
|
+
import { FSCodegen } from "../utils/fs-codegen.js";
|
|
19
20
|
export const graphqlLog = createConsole("GraphQL Codegen", "graphql");
|
|
20
21
|
const console = graphqlLog;
|
|
21
22
|
class GraphQLValidationError {
|
|
@@ -49,6 +50,7 @@ class GraphQLValidationError {
|
|
|
49
50
|
}
|
|
50
51
|
export class GraphQLGenerator {
|
|
51
52
|
project;
|
|
53
|
+
opts;
|
|
52
54
|
needsRegenerate = false;
|
|
53
55
|
needsReload = true;
|
|
54
56
|
regenerateReason = "";
|
|
@@ -56,11 +58,13 @@ export class GraphQLGenerator {
|
|
|
56
58
|
isGenerating = false;
|
|
57
59
|
schemaLoader;
|
|
58
60
|
fileLoader;
|
|
61
|
+
optimizeWriter;
|
|
59
62
|
debouncer;
|
|
60
63
|
retryTimer;
|
|
61
|
-
constructor(project) {
|
|
64
|
+
constructor(project, opts) {
|
|
62
65
|
this.project = project;
|
|
63
|
-
this.
|
|
66
|
+
this.opts = opts;
|
|
67
|
+
this.schemaLoader = new GraphQLSchemaLoader(ProjectEnvUtils.getSafe("DEBUG_GRAPHQL_URL"));
|
|
64
68
|
this.fileLoader = createGraphQLFileLoader(this.project, [
|
|
65
69
|
{
|
|
66
70
|
baseFolder: "queries/fragments",
|
|
@@ -83,6 +87,38 @@ export class GraphQLGenerator {
|
|
|
83
87
|
key: "queries",
|
|
84
88
|
},
|
|
85
89
|
]);
|
|
90
|
+
this.optimizeWriter = new FSCodegen(project, {
|
|
91
|
+
outDir: "./.eddev/queries",
|
|
92
|
+
});
|
|
93
|
+
this.optimizeWriter.register({
|
|
94
|
+
getFiles: async () => {
|
|
95
|
+
const files = await this.fileLoader.get();
|
|
96
|
+
const { queries, fragments } = this.getOptimizedQueries(files, true);
|
|
97
|
+
const optimizedQueries = Object.entries(queries).flatMap(([_, group]) => {
|
|
98
|
+
return Object.entries(group).flatMap(([name, ast]) => {
|
|
99
|
+
let content = [
|
|
100
|
+
ast.leadingComments ? ast.leadingComments + "\n" : "",
|
|
101
|
+
print(ast),
|
|
102
|
+
ast.trailingComments ? "\n" + ast.trailingComments : "",
|
|
103
|
+
].join("");
|
|
104
|
+
return {
|
|
105
|
+
name,
|
|
106
|
+
content,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
return [
|
|
111
|
+
...optimizedQueries,
|
|
112
|
+
{
|
|
113
|
+
name: "fragments.json",
|
|
114
|
+
content: JSON.stringify(Object.values(fragments).map(print), null, 2),
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
},
|
|
118
|
+
susbcribe: (callback) => {
|
|
119
|
+
return this.fileLoader.subscribe(callback);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
86
122
|
}
|
|
87
123
|
async queueRegenerate(full, reason, info) {
|
|
88
124
|
this.needsRegenerate = true;
|
|
@@ -107,14 +143,26 @@ export class GraphQLGenerator {
|
|
|
107
143
|
// Do initial load of blocks
|
|
108
144
|
await this.project.blocks.get();
|
|
109
145
|
await this.project.views.get();
|
|
110
|
-
|
|
111
|
-
|
|
146
|
+
if (this.opts.generate) {
|
|
147
|
+
try {
|
|
148
|
+
await this.generate();
|
|
149
|
+
}
|
|
150
|
+
catch (err) { }
|
|
151
|
+
}
|
|
152
|
+
if (this.opts.watch) {
|
|
153
|
+
this.project.blocks.subscribeFuture(() => {
|
|
154
|
+
this.generateUtilTypes();
|
|
155
|
+
});
|
|
156
|
+
this.listen();
|
|
157
|
+
}
|
|
158
|
+
if (this.opts.optimize) {
|
|
159
|
+
if (this.opts.watch) {
|
|
160
|
+
await this.optimizeWriter.runAndWatch();
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
await this.optimizeWriter.run();
|
|
164
|
+
}
|
|
112
165
|
}
|
|
113
|
-
catch (err) { }
|
|
114
|
-
this.project.blocks.subscribeFuture(() => {
|
|
115
|
-
this.generateUtilTypes();
|
|
116
|
-
});
|
|
117
|
-
this.listen();
|
|
118
166
|
}
|
|
119
167
|
async listen() {
|
|
120
168
|
// Subscribe to changes in block names
|
|
@@ -138,14 +186,23 @@ export class GraphQLGenerator {
|
|
|
138
186
|
this.queueRegenerate(false, "GraphQL file(s) modifed", events?.map(stringifyFileEvent));
|
|
139
187
|
});
|
|
140
188
|
// Look out for PHP and acf-json file changes
|
|
141
|
-
watchFileTreeForChanges(
|
|
142
|
-
|
|
189
|
+
watchFileTreeForChanges({
|
|
190
|
+
pattern: ["backend/**/*.php"],
|
|
191
|
+
callback: (event) => {
|
|
192
|
+
this.queueRegenerate(true, "PHP file change detected", stringifyFileEvent(event));
|
|
193
|
+
},
|
|
143
194
|
});
|
|
144
|
-
watchFileTreeForChanges(
|
|
145
|
-
|
|
195
|
+
watchFileTreeForChanges({
|
|
196
|
+
pattern: ["vendor/**/*.php"],
|
|
197
|
+
callback: (change) => {
|
|
198
|
+
this.queueRegenerate(true, "PHP Vendor folder change detected", stringifyFileEvent(change));
|
|
199
|
+
},
|
|
146
200
|
});
|
|
147
|
-
watchFileTreeForChanges(
|
|
148
|
-
|
|
201
|
+
watchFileTreeForChanges({
|
|
202
|
+
pattern: ["acf-json/**/*.json"],
|
|
203
|
+
callback: (change) => {
|
|
204
|
+
this.queueRegenerate(true, "ACF Field change detected", stringifyFileEvent(change));
|
|
205
|
+
},
|
|
149
206
|
});
|
|
150
207
|
}
|
|
151
208
|
scheduleRetry() {
|
|
@@ -337,14 +394,9 @@ export class GraphQLGenerator {
|
|
|
337
394
|
this.generate();
|
|
338
395
|
}
|
|
339
396
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
validateGraphQLFiles(graphQLFiles, schema) {
|
|
344
|
-
/**
|
|
345
|
-
* Use all rules, except for No Unused Fragments — since we prepend those fragments to every test
|
|
346
|
-
*/
|
|
347
|
-
const validationRules = specifiedRules.filter((rule) => rule !== NoUnusedFragmentsRule);
|
|
397
|
+
getOptimizedQueries(graphQLFiles, rewrite = false) {
|
|
398
|
+
const result = {};
|
|
399
|
+
const allFragments = {};
|
|
348
400
|
/**
|
|
349
401
|
* Get all the base fragment definitions
|
|
350
402
|
*/
|
|
@@ -355,7 +407,11 @@ export class GraphQLGenerator {
|
|
|
355
407
|
.flat()
|
|
356
408
|
.filter((n) => n.kind === Kind.FRAGMENT_DEFINITION)
|
|
357
409
|
.forEach((node) => baseFragments.set(node.name.value, node));
|
|
410
|
+
baseFragments.forEach((node) => {
|
|
411
|
+
allFragments[node.name.value] = node;
|
|
412
|
+
});
|
|
358
413
|
function findFragments(defs, map = new Map()) {
|
|
414
|
+
// console.log("findFragments", defs.length)
|
|
359
415
|
for (let def of defs) {
|
|
360
416
|
if (def.kind === Kind.FRAGMENT_DEFINITION) {
|
|
361
417
|
map.set(def.name.value, def);
|
|
@@ -379,6 +435,40 @@ export class GraphQLGenerator {
|
|
|
379
435
|
definitions: defs.filter((n) => n.kind !== Kind.FRAGMENT_DEFINITION),
|
|
380
436
|
};
|
|
381
437
|
}
|
|
438
|
+
for (let [groupName, fileMap] of Object.entries(graphQLFiles)) {
|
|
439
|
+
result[groupName] = {};
|
|
440
|
+
for (let [path, file] of Object.entries(fileMap)) {
|
|
441
|
+
if (!file.parseError) {
|
|
442
|
+
// Find all fragments
|
|
443
|
+
const { fragments, definitions } = findFragments(file?.ast?.definitions ?? []);
|
|
444
|
+
const ast = {
|
|
445
|
+
kind: Kind.DOCUMENT,
|
|
446
|
+
definitions: [...definitions, ...fragments],
|
|
447
|
+
loc: file.ast.loc,
|
|
448
|
+
leadingComments: file.leadingComments,
|
|
449
|
+
trailingComments: file.trailingComments,
|
|
450
|
+
};
|
|
451
|
+
result[groupName][path] = ast;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
queries: result,
|
|
457
|
+
fragments: allFragments,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Validates a GraphQL manifest against a schema, ensuring there are no user errors.
|
|
462
|
+
*/
|
|
463
|
+
validateGraphQLFiles(graphQLFiles, schema) {
|
|
464
|
+
/**
|
|
465
|
+
* Use all rules, except for No Unused Fragments — since we prepend those fragments to every test
|
|
466
|
+
*/
|
|
467
|
+
const validationRules = specifiedRules.filter((rule) => rule !== NoUnusedFragmentsRule);
|
|
468
|
+
/**
|
|
469
|
+
* Get the optimized files — queries with all fragments included
|
|
470
|
+
*/
|
|
471
|
+
const { queries } = this.getOptimizedQueries(graphQLFiles);
|
|
382
472
|
/**
|
|
383
473
|
* Only report the same error once (mostly for preventing repeats of the same fragment errors)
|
|
384
474
|
*/
|
|
@@ -388,18 +478,17 @@ export class GraphQLGenerator {
|
|
|
388
478
|
*/
|
|
389
479
|
const report = [];
|
|
390
480
|
for (let [groupName, fileMap] of Object.entries(graphQLFiles)) {
|
|
481
|
+
const group = queries[groupName];
|
|
391
482
|
for (let [path, file] of Object.entries(fileMap)) {
|
|
392
483
|
if (file.parseError) {
|
|
393
484
|
report.push(new GraphQLValidationError(file.parseError.message, path, file.parseError.line, file.parseError.column, file.contents));
|
|
394
485
|
}
|
|
395
486
|
else {
|
|
396
487
|
// Find all fragments
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
loc: file.ast.loc,
|
|
402
|
-
};
|
|
488
|
+
const validationAST = group[path];
|
|
489
|
+
if (!validationAST) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
403
492
|
const errors = validate(schema, validationAST, validationRules);
|
|
404
493
|
if (errors.length > 0) {
|
|
405
494
|
for (let error of errors) {
|
|
@@ -67,7 +67,7 @@ export default {
|
|
|
67
67
|
// Another convention, the query name and file name should be the same
|
|
68
68
|
const baseName = basename(fileName);
|
|
69
69
|
if (baseName.replace(".graphql", "") !== name) {
|
|
70
|
-
throwErrorAtLocation(item.name.loc, `Expected
|
|
70
|
+
throwErrorAtLocation(item.name.loc, `Expected GraphQL operation name to match the filename.\nYou should either:\na) rename the file to '${name}.graphql'\nb) rename the operation to '${baseName}'`);
|
|
71
71
|
}
|
|
72
72
|
// Only import variables if the operation has variables
|
|
73
73
|
if (variablesTypeName) {
|
|
@@ -10,6 +10,8 @@ type Entry = {
|
|
|
10
10
|
line?: number;
|
|
11
11
|
column?: number;
|
|
12
12
|
} | null;
|
|
13
|
+
leadingComments?: string;
|
|
14
|
+
trailingComments?: string;
|
|
13
15
|
};
|
|
14
16
|
export type GraphQLManifest<TKeys extends string> = {
|
|
15
17
|
[K in TKeys]: {
|
|
@@ -21,6 +23,7 @@ type PatternMap<TKeys extends string> = {
|
|
|
21
23
|
key: TKeys;
|
|
22
24
|
baseFolder: string;
|
|
23
25
|
pattern: string;
|
|
26
|
+
ignore?: string[];
|
|
24
27
|
}[];
|
|
25
28
|
export declare function createGraphQLFileLoader<TKeys extends string>(project: Project, patterns: PatternMap<TKeys>): import("../project/manifest/manifest.js").ManifestGenerator<Entry, GraphQLManifest<TKeys>>;
|
|
26
29
|
export {};
|
|
@@ -7,6 +7,7 @@ export function createGraphQLFileLoader(project, patterns) {
|
|
|
7
7
|
return createManifestGenerator({
|
|
8
8
|
baseDir: project.rootDir,
|
|
9
9
|
pattern: patterns.map((p) => join(p.baseFolder, p.pattern)),
|
|
10
|
+
ignore: patterns.flatMap((p) => p.ignore?.map((ignore) => join(p.baseFolder, ignore)) ?? []),
|
|
10
11
|
watch: cliMode.watch,
|
|
11
12
|
async generateManifest(entries) {
|
|
12
13
|
let result = {};
|
|
@@ -32,6 +33,8 @@ export function createGraphQLFileLoader(project, patterns) {
|
|
|
32
33
|
const contents = await fs.readFile(path, "utf8");
|
|
33
34
|
let ast = null;
|
|
34
35
|
let parseError = null;
|
|
36
|
+
const leadingComments = contents.match(/^(\n|#[^\n]*)+\n/)?.[0].trim();
|
|
37
|
+
const trailingComments = contents.match(/(\n|#[^\n]*)+$/)?.[0].trim();
|
|
35
38
|
try {
|
|
36
39
|
ast = parse(contents);
|
|
37
40
|
}
|
|
@@ -48,6 +51,8 @@ export function createGraphQLFileLoader(project, patterns) {
|
|
|
48
51
|
contents,
|
|
49
52
|
ast,
|
|
50
53
|
parseError,
|
|
54
|
+
leadingComments,
|
|
55
|
+
trailingComments,
|
|
51
56
|
};
|
|
52
57
|
},
|
|
53
58
|
});
|
|
@@ -8,6 +8,7 @@ type ManifestEntry<TEntryData> = {
|
|
|
8
8
|
type ManifestGeneratorArgs<TEntryData, TManifest> = {
|
|
9
9
|
baseDir: string;
|
|
10
10
|
pattern: string | string[];
|
|
11
|
+
ignore?: string[];
|
|
11
12
|
watch: boolean;
|
|
12
13
|
loadValue: (path: string, key: string) => Promise<TEntryData>;
|
|
13
14
|
getKey: (path: string) => string | null;
|
|
@@ -31,20 +31,24 @@ export function createManifestGenerator(args) {
|
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
if (args.watch) {
|
|
34
|
-
const disposer = await watchFileTree(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
fileList
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
const disposer = await watchFileTree({
|
|
35
|
+
pattern: args.pattern,
|
|
36
|
+
ignore: args.ignore,
|
|
37
|
+
callback: (fileList, event) => {
|
|
38
|
+
files = fileList;
|
|
39
|
+
cache = new Map();
|
|
40
|
+
if (args.onFileChange)
|
|
41
|
+
fileList.forEach(args.onFileChange);
|
|
42
|
+
update();
|
|
43
|
+
if (event) {
|
|
44
|
+
events.push(event);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
43
47
|
});
|
|
44
48
|
disposers.push(disposer);
|
|
45
49
|
}
|
|
46
50
|
else {
|
|
47
|
-
files = await getFileTree(args.pattern);
|
|
51
|
+
files = await getFileTree({ pattern: args.pattern, ignore: args.ignore });
|
|
48
52
|
update();
|
|
49
53
|
}
|
|
50
54
|
});
|
|
@@ -15,6 +15,7 @@ export declare class FSCodegen {
|
|
|
15
15
|
project: Project;
|
|
16
16
|
opts: {
|
|
17
17
|
outDir: string;
|
|
18
|
+
onWritten?: (opts: Set<string>) => void;
|
|
18
19
|
};
|
|
19
20
|
outDir: string;
|
|
20
21
|
private generators;
|
|
@@ -23,6 +24,7 @@ export declare class FSCodegen {
|
|
|
23
24
|
private flushScheduled;
|
|
24
25
|
constructor(project: Project, opts: {
|
|
25
26
|
outDir: string;
|
|
27
|
+
onWritten?: (opts: Set<string>) => void;
|
|
26
28
|
});
|
|
27
29
|
register(generator: CodeGenerator): void;
|
|
28
30
|
registerFile({ name, generate, subscribe, }: {
|
|
@@ -77,7 +77,7 @@ export class FSCodegen {
|
|
|
77
77
|
}
|
|
78
78
|
async flush() {
|
|
79
79
|
const files = Array.from(this.generatorFiles.values()).flat();
|
|
80
|
-
const existing = (await globby(this.outDir
|
|
80
|
+
const existing = (await globby(join(this.outDir, "/**/*"))).map((path) => resolve(this.project.rootDir, path));
|
|
81
81
|
const written = new Set();
|
|
82
82
|
await Promise.all(files.map(async (file) => {
|
|
83
83
|
const path = join(this.project.rootDir, this.outDir, file.name);
|
|
@@ -89,6 +89,7 @@ export class FSCodegen {
|
|
|
89
89
|
await fs.rm(path);
|
|
90
90
|
}
|
|
91
91
|
}));
|
|
92
|
+
this.opts.onWritten?.(written);
|
|
92
93
|
}
|
|
93
94
|
stop() {
|
|
94
95
|
this.unsubscribers.forEach((unsub) => unsub());
|
|
@@ -5,7 +5,21 @@ export type FileEvent = {
|
|
|
5
5
|
type: FileWatchEvent;
|
|
6
6
|
file: string;
|
|
7
7
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
type WatchTreeOpts = {
|
|
9
|
+
pattern: string | string[];
|
|
10
|
+
ignore?: string[];
|
|
11
|
+
callback: (files: FileList, event?: FileEvent) => void;
|
|
12
|
+
};
|
|
13
|
+
export declare function watchFileTree({ pattern, callback, ignore }: WatchTreeOpts): Promise<Disposer>;
|
|
14
|
+
type WatchTreeChangesOpts = {
|
|
15
|
+
pattern: string | string[];
|
|
16
|
+
ignore?: string | RegExp | string[];
|
|
17
|
+
callback: (event: FileEvent) => void;
|
|
18
|
+
};
|
|
19
|
+
export declare function watchFileTreeForChanges({ pattern, callback, ignore }: WatchTreeChangesOpts): Promise<Disposer>;
|
|
20
|
+
type GetTreeOpts = {
|
|
21
|
+
pattern: string | string[];
|
|
22
|
+
ignore?: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare function getFileTree(opts: GetTreeOpts): Promise<FileList>;
|
|
11
25
|
export {};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
2
|
import { globby } from "globby";
|
|
3
|
-
export function watchFileTree(pattern, callback) {
|
|
3
|
+
export function watchFileTree({ pattern, callback, ignore }) {
|
|
4
4
|
const files = [];
|
|
5
5
|
let ready = false;
|
|
6
6
|
return new Promise((resolve) => {
|
|
7
7
|
const watcher = chokidar
|
|
8
|
-
.watch(pattern
|
|
8
|
+
.watch(pattern, {
|
|
9
|
+
ignored: ignore,
|
|
10
|
+
})
|
|
9
11
|
.on("add", (path) => {
|
|
10
12
|
files.push(path);
|
|
11
13
|
if (ready)
|
|
@@ -31,7 +33,7 @@ export function watchFileTree(pattern, callback) {
|
|
|
31
33
|
});
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
|
-
export function watchFileTreeForChanges(pattern, callback) {
|
|
36
|
+
export function watchFileTreeForChanges({ pattern, callback, ignore }) {
|
|
35
37
|
let ready = false;
|
|
36
38
|
return new Promise((resolve) => {
|
|
37
39
|
const watcher = chokidar
|
|
@@ -50,9 +52,14 @@ export function watchFileTreeForChanges(pattern, callback) {
|
|
|
50
52
|
})
|
|
51
53
|
.on("ready", () => {
|
|
52
54
|
ready = true;
|
|
55
|
+
resolve(() => {
|
|
56
|
+
watcher.close();
|
|
57
|
+
});
|
|
53
58
|
});
|
|
54
59
|
});
|
|
55
60
|
}
|
|
56
|
-
export function getFileTree(
|
|
57
|
-
return globby(pattern
|
|
61
|
+
export function getFileTree(opts) {
|
|
62
|
+
return globby(opts.pattern, {
|
|
63
|
+
ignore: opts.ignore,
|
|
64
|
+
});
|
|
58
65
|
}
|