@atcute/lex-cli 2.4.0 → 2.5.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 +40 -8
- package/dist/cli.js +10 -168
- package/dist/cli.js.map +1 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +76 -78
- package/dist/codegen.js.map +1 -1
- package/dist/commands/export.d.ts +13 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +76 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/generate.d.ts +13 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +136 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/pull.d.ts +13 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/{pull.js → commands/pull.js} +35 -81
- package/dist/commands/pull.js.map +1 -0
- package/dist/config.d.ts +68 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +54 -3
- package/dist/config.js.map +1 -1
- package/dist/lexicon-loader.d.ts +17 -0
- package/dist/lexicon-loader.d.ts.map +1 -0
- package/dist/lexicon-loader.js +167 -0
- package/dist/lexicon-loader.js.map +1 -0
- package/dist/pull-sources/atproto.d.ts +9 -0
- package/dist/pull-sources/atproto.d.ts.map +1 -0
- package/dist/pull-sources/atproto.js +192 -0
- package/dist/pull-sources/atproto.js.map +1 -0
- package/dist/pull-sources/git.d.ts +11 -0
- package/dist/pull-sources/git.d.ts.map +1 -0
- package/dist/pull-sources/git.js +80 -0
- package/dist/pull-sources/git.js.map +1 -0
- package/dist/pull-sources/types.d.ts +16 -0
- package/dist/pull-sources/types.d.ts.map +1 -0
- package/dist/pull-sources/types.js +2 -0
- package/dist/pull-sources/types.js.map +1 -0
- package/dist/shared-options.d.ts +6 -0
- package/dist/shared-options.d.ts.map +1 -0
- package/dist/shared-options.js +11 -0
- package/dist/shared-options.js.map +1 -0
- package/package.json +10 -7
- package/src/cli.ts +9 -210
- package/src/codegen.ts +90 -88
- package/src/commands/export.ts +106 -0
- package/src/commands/generate.ts +170 -0
- package/src/{pull.ts → commands/pull.ts} +49 -116
- package/src/config.ts +67 -4
- package/src/lexicon-loader.ts +199 -0
- package/src/pull-sources/atproto.ts +243 -0
- package/src/pull-sources/git.ts +103 -0
- package/src/pull-sources/types.ts +18 -0
- package/src/shared-options.ts +13 -0
- package/dist/pull.d.ts +0 -7
- package/dist/pull.d.ts.map +0 -1
- package/dist/pull.js.map +0 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LexiconDoc } from '@atcute/lexicon-doc';
|
|
2
|
+
import type { GitSourceConfig } from '../config.js';
|
|
3
|
+
import type { PullResult, SourceLocation } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* pulls lexicon documents from a git repository source
|
|
6
|
+
* @param source git source configuration
|
|
7
|
+
* @param parseLexiconFile function to parse and validate lexicon files
|
|
8
|
+
* @returns pulled lexicons and commit hash
|
|
9
|
+
*/
|
|
10
|
+
export declare const pullGitSource: (source: GitSourceConfig, parseLexiconFile: (loc: SourceLocation) => Promise<LexiconDoc>) => Promise<PullResult>;
|
|
11
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/pull-sources/git.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAiB,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACzB,QAAQ,eAAe,EACvB,kBAAkB,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,UAAU,CAAC,KAC5D,OAAO,CAAC,UAAU,CAkFpB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { runGit, GitError } from '../git.js';
|
|
6
|
+
/**
|
|
7
|
+
* pulls lexicon documents from a git repository source
|
|
8
|
+
* @param source git source configuration
|
|
9
|
+
* @param parseLexiconFile function to parse and validate lexicon files
|
|
10
|
+
* @returns pulled lexicons and commit hash
|
|
11
|
+
*/
|
|
12
|
+
export const pullGitSource = async (source, parseLexiconFile) => {
|
|
13
|
+
const tempParent = await fs.mkdtemp(path.join(os.tmpdir(), 'lex-cli-pull-'));
|
|
14
|
+
const cloneDir = path.join(tempParent, 'repo');
|
|
15
|
+
try {
|
|
16
|
+
await runGit([
|
|
17
|
+
'clone',
|
|
18
|
+
'--filter=blob:none',
|
|
19
|
+
'--depth',
|
|
20
|
+
'1',
|
|
21
|
+
'--sparse',
|
|
22
|
+
...(source.ref ? ['--branch', source.ref, '--single-branch'] : []),
|
|
23
|
+
source.remote,
|
|
24
|
+
cloneDir,
|
|
25
|
+
], { timeoutMs: 60_000 });
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err instanceof GitError) {
|
|
29
|
+
console.error(pc.bold(pc.red(`git clone failed for ${source.remote}:`)));
|
|
30
|
+
console.error(err.stderr || err.message);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
await runGit(['-C', cloneDir, 'sparse-checkout', 'set', '--no-cone', ...source.pattern], {
|
|
37
|
+
timeoutMs: 30_000,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err instanceof GitError) {
|
|
42
|
+
console.error(pc.bold(pc.red(`git sparse-checkout failed for ${source.remote}:`)));
|
|
43
|
+
console.error(err.stderr || err.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
const pulled = new Map();
|
|
49
|
+
for await (const filename of fs.glob(source.pattern, { cwd: cloneDir })) {
|
|
50
|
+
const absolute = path.join(cloneDir, filename);
|
|
51
|
+
const stat = await fs.stat(absolute);
|
|
52
|
+
if (!stat.isFile()) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const location = {
|
|
56
|
+
absolutePath: absolute,
|
|
57
|
+
relativePath: filename,
|
|
58
|
+
sourceDescription: source.remote,
|
|
59
|
+
};
|
|
60
|
+
const doc = await parseLexiconFile(location);
|
|
61
|
+
pulled.set(doc.id, { nsid: doc.id, doc, location });
|
|
62
|
+
}
|
|
63
|
+
// get the commit hash
|
|
64
|
+
let rev;
|
|
65
|
+
try {
|
|
66
|
+
const result = await runGit(['-C', cloneDir, 'rev-parse', 'HEAD'], { timeoutMs: 10_000 });
|
|
67
|
+
rev = result.stdout.trim();
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err instanceof GitError) {
|
|
71
|
+
console.error(pc.bold(pc.red(`git rev-parse failed for ${source.remote}:`)));
|
|
72
|
+
console.error(err.stderr || err.message);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
await fs.rm(tempParent, { recursive: true, force: true });
|
|
78
|
+
return { pulled, rev };
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/pull-sources/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LexiconDoc } from '@atcute/lexicon-doc';
|
|
2
|
+
export interface SourceLocation {
|
|
3
|
+
absolutePath: string;
|
|
4
|
+
relativePath: string;
|
|
5
|
+
sourceDescription: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PulledLexicon {
|
|
8
|
+
nsid: string;
|
|
9
|
+
doc: LexiconDoc;
|
|
10
|
+
location: SourceLocation;
|
|
11
|
+
}
|
|
12
|
+
export interface PullResult {
|
|
13
|
+
pulled: Map<string, PulledLexicon>;
|
|
14
|
+
rev?: string;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/pull-sources/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,UAAU,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pull-sources/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const sharedOptions: import("@optique/core/parser").Parser<{
|
|
2
|
+
readonly config: string | undefined;
|
|
3
|
+
}, {
|
|
4
|
+
readonly config: [import("@optique/core/valueparser").ValueParserResult<string> | undefined] | undefined;
|
|
5
|
+
}>;
|
|
6
|
+
//# sourceMappingURL=shared-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-options.d.ts","sourceRoot":"","sources":["../src/shared-options.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa;;;;EAMxB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { object } from '@optique/core/constructs';
|
|
2
|
+
import { message } from '@optique/core/message';
|
|
3
|
+
import { optional } from '@optique/core/modifiers';
|
|
4
|
+
import { option } from '@optique/core/primitives';
|
|
5
|
+
import { path as pathParser } from '@optique/run/valueparser';
|
|
6
|
+
export const sharedOptions = object(`Global options`, {
|
|
7
|
+
config: optional(option('-c', '--config', pathParser({ metavar: 'CONFIG' }), {
|
|
8
|
+
description: message `path to the lexicon configuration file. defaults to searching for lex.config.js or lex.config.ts in the current directory.`,
|
|
9
|
+
})),
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=shared-options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-options.js","sourceRoot":"","sources":["../src/shared-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,gBAAgB,EAAE;IACrD,MAAM,EAAE,QAAQ,CACf,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE;QAC3D,WAAW,EAAE,OAAO,CAAA,4HAA4H;KAChJ,CAAC,CACF;CACD,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@atcute/lex-cli",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"description": "cli tool to generate type definitions for atcute",
|
|
6
6
|
"license": "0BSD",
|
|
7
7
|
"repository": {
|
|
@@ -22,16 +22,19 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@badrap/valita": "^0.4.6",
|
|
25
|
-
"@optique/core": "^0.6.
|
|
26
|
-
"@optique/run": "^0.6.
|
|
25
|
+
"@optique/core": "^0.6.3",
|
|
26
|
+
"@optique/run": "^0.6.3",
|
|
27
27
|
"picocolors": "^1.1.1",
|
|
28
28
|
"prettier": "^3.6.2",
|
|
29
|
-
"@atcute/
|
|
29
|
+
"@atcute/identity": "^1.1.3",
|
|
30
|
+
"@atcute/lexicon-doc": "^2.0.3",
|
|
31
|
+
"@atcute/identity-resolver": "^1.1.4",
|
|
32
|
+
"@atcute/lexicon-resolver": "^0.1.4",
|
|
33
|
+
"@atcute/lexicons": "^1.2.5"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^22.19.
|
|
33
|
-
"tschema": "^3.2.0"
|
|
34
|
-
"@atcute/lexicons": "^1.2.5"
|
|
36
|
+
"@types/node": "^22.19.1",
|
|
37
|
+
"tschema": "^3.2.0"
|
|
35
38
|
},
|
|
36
39
|
"scripts": {
|
|
37
40
|
"build": "pnpm run generate:schema && tsc",
|
package/src/cli.ts
CHANGED
|
@@ -1,219 +1,18 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { lexiconDoc, refineLexiconDoc, type LexiconDoc } from '@atcute/lexicon-doc';
|
|
5
|
-
|
|
6
|
-
import { object } from '@optique/core/constructs';
|
|
7
|
-
import { command, constant, option } from '@optique/core/primitives';
|
|
8
1
|
import { or } from '@optique/core/constructs';
|
|
9
2
|
import { run } from '@optique/run';
|
|
10
|
-
import { path as pathParser } from '@optique/run/valueparser';
|
|
11
|
-
import pc from 'picocolors';
|
|
12
|
-
|
|
13
|
-
import { generateLexiconApi, type ImportMapping } from './codegen.js';
|
|
14
|
-
import { loadConfig } from './config.js';
|
|
15
|
-
import { packageJsonSchema } from './lexicon-metadata.js';
|
|
16
|
-
import { runPull } from './pull.js';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Resolves package imports to ImportMapping[]
|
|
20
|
-
*/
|
|
21
|
-
const resolveImportsToMappings = async (
|
|
22
|
-
imports: string[],
|
|
23
|
-
configDirname: string,
|
|
24
|
-
): Promise<ImportMapping[]> => {
|
|
25
|
-
const mappings: ImportMapping[] = [];
|
|
26
|
-
|
|
27
|
-
for (const packageName of imports) {
|
|
28
|
-
// Walk up from config directory to find package in node_modules
|
|
29
|
-
let packageJson: unknown;
|
|
30
|
-
let currentDir = configDirname;
|
|
31
|
-
let found = false;
|
|
32
|
-
|
|
33
|
-
while (currentDir !== path.dirname(currentDir)) {
|
|
34
|
-
const candidatePath = path.join(currentDir, 'node_modules', packageName, 'package.json');
|
|
35
|
-
try {
|
|
36
|
-
const content = await fs.readFile(candidatePath, 'utf8');
|
|
37
|
-
packageJson = JSON.parse(content);
|
|
38
|
-
found = true;
|
|
39
|
-
break;
|
|
40
|
-
} catch (err: any) {
|
|
41
|
-
// Only continue to parent if file not found
|
|
42
|
-
if (err.code !== 'ENOENT') {
|
|
43
|
-
console.error(pc.bold(pc.red(`failed to read package.json for "${packageName}":`)));
|
|
44
|
-
console.error(err);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Not found, try parent directory
|
|
49
|
-
currentDir = path.dirname(currentDir);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!found) {
|
|
54
|
-
console.error(pc.bold(pc.red(`failed to resolve package "${packageName}"`)));
|
|
55
|
-
console.error(`Could not find package in node_modules starting from ${configDirname}`);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Validate package.json
|
|
60
|
-
const result = packageJsonSchema.try(packageJson, { mode: 'passthrough' });
|
|
61
|
-
if (!result.ok) {
|
|
62
|
-
console.error(pc.bold(pc.red(`invalid atcute:lexicons in "${packageName}":`)));
|
|
63
|
-
console.error(result.message);
|
|
64
|
-
|
|
65
|
-
for (const issue of result.issues) {
|
|
66
|
-
console.log(`- ${issue.code} at .${issue.path.join('.')}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const lexicons = result.value['atcute:lexicons'];
|
|
73
|
-
if (!lexicons?.mappings) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Convert mapping to ImportMapping[]
|
|
78
|
-
for (const [pattern, entry] of Object.entries(lexicons.mappings)) {
|
|
79
|
-
const isWildcard = pattern.endsWith('.*');
|
|
80
|
-
|
|
81
|
-
mappings.push({
|
|
82
|
-
nsid: [pattern],
|
|
83
|
-
imports: (nsid: string) => {
|
|
84
|
-
// Check if pattern matches
|
|
85
|
-
if (isWildcard) {
|
|
86
|
-
if (!nsid.startsWith(pattern.slice(0, -1))) {
|
|
87
|
-
throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
if (nsid !== pattern) {
|
|
91
|
-
throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const nsidPrefix = isWildcard ? pattern.slice(0, -2) : pattern;
|
|
96
|
-
const nsidRemainder = isWildcard ? nsid.slice(nsidPrefix.length + 1) : '';
|
|
97
|
-
|
|
98
|
-
let expandedPath = entry.path
|
|
99
|
-
.replaceAll('{{nsid}}', nsid.replaceAll('.', '/'))
|
|
100
|
-
.replaceAll('{{nsid_remainder}}', nsidRemainder.replaceAll('.', '/'))
|
|
101
|
-
.replaceAll('{{nsid_prefix}}', nsidPrefix.replaceAll('.', '/'));
|
|
102
|
-
|
|
103
|
-
if (expandedPath === '.') {
|
|
104
|
-
expandedPath = packageName;
|
|
105
|
-
} else if (expandedPath.startsWith('./')) {
|
|
106
|
-
expandedPath = `${packageName}/${expandedPath.slice(2)}`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
type: entry.type,
|
|
111
|
-
from: expandedPath,
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
3
|
|
|
118
|
-
|
|
119
|
-
};
|
|
4
|
+
import { exportCommandSchema, runExport } from './commands/export.js';
|
|
5
|
+
import { generateCommandSchema, runGenerate } from './commands/generate.js';
|
|
6
|
+
import { pullCommandSchema, runPull } from './commands/pull.js';
|
|
120
7
|
|
|
121
|
-
const parser = or(
|
|
122
|
-
command(
|
|
123
|
-
'generate',
|
|
124
|
-
object({
|
|
125
|
-
type: constant('generate'),
|
|
126
|
-
config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
|
|
127
|
-
}),
|
|
128
|
-
),
|
|
129
|
-
command(
|
|
130
|
-
'pull',
|
|
131
|
-
object({
|
|
132
|
-
type: constant('pull'),
|
|
133
|
-
config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
|
|
134
|
-
}),
|
|
135
|
-
),
|
|
136
|
-
);
|
|
8
|
+
const parser = or(generateCommandSchema, pullCommandSchema, exportCommandSchema);
|
|
137
9
|
|
|
138
|
-
const result = run(parser, { programName: 'lex-cli' });
|
|
10
|
+
const result = run(parser, { programName: 'lex-cli', help: 'both' });
|
|
139
11
|
|
|
140
12
|
if (result.type === 'generate') {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Resolve imports to mappings
|
|
144
|
-
const importMappings = config.imports ? await resolveImportsToMappings(config.imports, config.root) : [];
|
|
145
|
-
const allMappings = [...importMappings, ...(config.mappings ?? [])];
|
|
146
|
-
|
|
147
|
-
const documents: LexiconDoc[] = [];
|
|
148
|
-
|
|
149
|
-
for await (const filename of fs.glob(config.files, { cwd: config.root })) {
|
|
150
|
-
let source: string;
|
|
151
|
-
try {
|
|
152
|
-
source = await fs.readFile(path.join(config.root, filename), 'utf8');
|
|
153
|
-
} catch (err) {
|
|
154
|
-
console.error(pc.bold(pc.red(`file read error with "${filename}"`)));
|
|
155
|
-
console.error(err);
|
|
156
|
-
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let json: unknown;
|
|
161
|
-
try {
|
|
162
|
-
json = JSON.parse(source);
|
|
163
|
-
} catch (err) {
|
|
164
|
-
console.error(pc.bold(pc.red(`json parse error in "${filename}"`)));
|
|
165
|
-
console.error(err);
|
|
166
|
-
|
|
167
|
-
process.exit(1);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const result = lexiconDoc.try(json, { mode: 'strip' });
|
|
171
|
-
if (!result.ok) {
|
|
172
|
-
console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`)));
|
|
173
|
-
console.error(result.message);
|
|
174
|
-
|
|
175
|
-
for (const issue of result.issues) {
|
|
176
|
-
console.log(`- ${issue.code} at .${issue.path.join('.')}`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
process.exit(1);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const issues = refineLexiconDoc(result.value, true);
|
|
183
|
-
if (issues.length > 0) {
|
|
184
|
-
console.error(pc.bold(pc.red(`lint validation failed for "${filename}"`)));
|
|
185
|
-
|
|
186
|
-
for (const issue of issues) {
|
|
187
|
-
console.log(`- ${issue.message} at .${issue.path.join('.')}`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
process.exit(1);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
documents.push(result.value);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const generationResult = await generateLexiconApi({
|
|
197
|
-
documents: documents,
|
|
198
|
-
mappings: allMappings,
|
|
199
|
-
modules: {
|
|
200
|
-
importSuffix: config.modules?.importSuffix ?? '.js',
|
|
201
|
-
},
|
|
202
|
-
prettier: {
|
|
203
|
-
cwd: process.cwd(),
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const outdir = path.join(config.root, config.outdir);
|
|
208
|
-
|
|
209
|
-
for (const file of generationResult.files) {
|
|
210
|
-
const filename = path.join(outdir, file.filename);
|
|
211
|
-
const dirname = path.dirname(filename);
|
|
212
|
-
|
|
213
|
-
await fs.mkdir(dirname, { recursive: true });
|
|
214
|
-
await fs.writeFile(filename, file.code);
|
|
215
|
-
}
|
|
13
|
+
await runGenerate(result);
|
|
216
14
|
} else if (result.type === 'pull') {
|
|
217
|
-
|
|
218
|
-
|
|
15
|
+
await runPull(result);
|
|
16
|
+
} else if (result.type === 'export') {
|
|
17
|
+
await runExport(result);
|
|
219
18
|
}
|