@atproto/lex-builder 0.0.23 → 0.1.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/CHANGELOG.md +18 -0
- package/dist/filter.js +1 -4
- package/dist/filter.js.map +1 -1
- package/dist/filtered-indexer.js +2 -8
- package/dist/filtered-indexer.js.map +1 -1
- package/dist/formatter.js +3 -11
- package/dist/formatter.js.map +1 -1
- package/dist/index.js +7 -11
- package/dist/index.js.map +1 -1
- package/dist/lex-builder.js +33 -38
- package/dist/lex-builder.js.map +1 -1
- package/dist/lex-def-builder.js +18 -26
- package/dist/lex-def-builder.js.map +1 -1
- package/dist/lexicon-directory-indexer.js +8 -12
- package/dist/lexicon-directory-indexer.js.map +1 -1
- package/dist/polyfill.d.ts +1 -0
- package/dist/polyfill.js +1 -1
- package/dist/polyfill.js.map +1 -1
- package/dist/ref-resolver.js +144 -152
- package/dist/ref-resolver.js.map +1 -1
- package/dist/ts-lang.js +5 -12
- package/dist/ts-lang.js.map +1 -1
- package/dist/util.js +13 -26
- package/dist/util.js.map +1 -1
- package/package.json +8 -9
- package/src/lex-builder.ts +1 -1
- package/src/ref-resolver.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atproto/lex-builder
|
|
2
2
|
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#4929](https://github.com/bluesky-social/atproto/pull/4929) [`f01c59f`](https://github.com/bluesky-social/atproto/commit/f01c59f5bd3f75fb8b47a9eecd4858b84033fb7c) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Drop support for Node.js 18 and 20. Node.js 22 is now the minimum supported version. Docker images now use Node.js 24.
|
|
8
|
+
|
|
9
|
+
- [#4943](https://github.com/bluesky-social/atproto/pull/4943) [`c459153`](https://github.com/bluesky-social/atproto/commit/c459153395a30ce89e050892c8fab7dc98e019b9) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Convert to pure ESM. All packages now ship `"type": "module"` with ES module output and Node16 module resolution.
|
|
10
|
+
|
|
11
|
+
Node.js 22's `require()` compatibility layer can still load these packages in CommonJS code.
|
|
12
|
+
|
|
13
|
+
- [#4930](https://github.com/bluesky-social/atproto/pull/4930) [`908bece`](https://github.com/bluesky-social/atproto/commit/908bece169258bff5ad121e5eec157d6ded6f705) Thanks [@devinivy](https://github.com/devinivy)! - Build with TypeScript 6.0.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`f01c59f`](https://github.com/bluesky-social/atproto/commit/f01c59f5bd3f75fb8b47a9eecd4858b84033fb7c), [`c459153`](https://github.com/bluesky-social/atproto/commit/c459153395a30ce89e050892c8fab7dc98e019b9), [`908bece`](https://github.com/bluesky-social/atproto/commit/908bece169258bff5ad121e5eec157d6ded6f705)]:
|
|
18
|
+
- @atproto/lex-document@0.1.0
|
|
19
|
+
- @atproto/lex-schema@0.1.0
|
|
20
|
+
|
|
3
21
|
## 0.0.23
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/filter.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildFilter = buildFilter;
|
|
4
1
|
/**
|
|
5
2
|
* Builds a filter function from include/exclude patterns.
|
|
6
3
|
*
|
|
@@ -23,7 +20,7 @@ exports.buildFilter = buildFilter;
|
|
|
23
20
|
* filter('com.atproto.repo') // false (not included)
|
|
24
21
|
* ```
|
|
25
22
|
*/
|
|
26
|
-
function buildFilter(options) {
|
|
23
|
+
export function buildFilter(options) {
|
|
27
24
|
const include = createMatcher(options.include, () => true);
|
|
28
25
|
const exclude = createMatcher(options.exclude, () => false);
|
|
29
26
|
return (id) => include(id) && !exclude(id);
|
package/dist/filter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAwCA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;IAE3D,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAC5C,CAAC;AAED,SAAS,aAAa,CACpB,OAAsC,EACtC,QAAgB;IAEhB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,QAAQ,CAAA;IACjB,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;IACzD,CAAC;SAAM,CAAC;QACN,OAAO,YAAY,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,CAAS,EAAE,CAAS;IAC1C,OAAO,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAA;AAChD,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,MAAM,CACtB,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAC5D,CAAA;QACD,OAAO,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC;IAED,OAAO,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,CAAA;AAC7C,CAAC","sourcesContent":["/**\n * Options for building a filter function to include/exclude lexicon documents.\n */\nexport type BuildFilterOptions = {\n /**\n * Pattern(s) for lexicon NSIDs to include.\n *\n * Supports glob patterns with `*` as a wildcard that matches one or more\n * characters. If not specified, all lexicons are included by default.\n *\n * @example\n * ```ts\n * { include: 'app.bsky.*' } // Include all app.bsky lexicons\n * { include: ['com.atproto.*', 'app.bsky.*'] } // Include multiple patterns\n * ```\n */\n include?: string | string[]\n /**\n * Pattern(s) for lexicon NSIDs to exclude.\n *\n * Supports glob patterns with `*` as a wildcard. Exclusions are applied\n * after inclusions.\n *\n * @example\n * ```ts\n * { exclude: '*.internal.*' } // Exclude internal lexicons\n * { exclude: ['*.test', '*.mock'] } // Exclude test and mock lexicons\n * ```\n */\n exclude?: string | string[]\n}\n\n/**\n * A function that tests whether a lexicon NSID should be included.\n *\n * @param input - The lexicon NSID to test\n * @returns `true` if the NSID passes the filter, `false` otherwise\n */\nexport type Filter = (input: string) => boolean\n\n/**\n * Builds a filter function from include/exclude patterns.\n *\n * The returned filter returns `true` for NSIDs that match any include pattern\n * (or all NSIDs if no include patterns are specified) AND do not match any\n * exclude pattern.\n *\n * @param options - The filter options containing include/exclude patterns\n * @returns A filter function that can be applied to lexicon NSIDs\n *\n * @example\n * ```ts\n * const filter = buildFilter({\n * include: 'app.bsky.*',\n * exclude: '*.internal.*',\n * })\n *\n * filter('app.bsky.feed.post') // true\n * filter('app.bsky.internal.foo') // false\n * filter('com.atproto.repo') // false (not included)\n * ```\n */\nexport function buildFilter(options: BuildFilterOptions): Filter {\n const include = createMatcher(options.include, () => true)\n const exclude = createMatcher(options.exclude, () => false)\n\n return (id) => include(id) && !exclude(id)\n}\n\nfunction createMatcher(\n pattern: undefined | string | string[],\n fallback: Filter,\n): Filter {\n if (!pattern?.length) {\n return fallback\n } else if (Array.isArray(pattern)) {\n return pattern.map(buildMatcher).reduce(combineFilters)\n } else {\n return buildMatcher(pattern)\n }\n}\n\nfunction combineFilters(a: Filter, b: Filter): Filter {\n return (input: string) => a(input) || b(input)\n}\n\nfunction buildMatcher(pattern: string): Filter {\n if (pattern.includes('*')) {\n const regex = new RegExp(\n `^${pattern.replaceAll('.', '\\\\.').replaceAll('*', '.+')}$`,\n )\n return (input: string) => regex.test(input)\n }\n\n return (input: string) => pattern === input\n}\n"]}
|
package/dist/filtered-indexer.js
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FilteredIndexer = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* A lexicon indexer that filters documents based on a provided filter.
|
|
6
3
|
*
|
|
7
4
|
* If a document was filtered out but later requested via `get()`, the filter
|
|
8
5
|
* will be bypassed for that document.
|
|
9
6
|
*/
|
|
10
|
-
class FilteredIndexer {
|
|
11
|
-
indexer;
|
|
12
|
-
filter;
|
|
13
|
-
returned = new Set();
|
|
7
|
+
export class FilteredIndexer {
|
|
14
8
|
constructor(indexer, filter) {
|
|
15
9
|
this.indexer = indexer;
|
|
16
10
|
this.filter = filter;
|
|
11
|
+
this.returned = new Set();
|
|
17
12
|
}
|
|
18
13
|
async get(id) {
|
|
19
14
|
this.returned.add(id);
|
|
@@ -52,5 +47,4 @@ class FilteredIndexer {
|
|
|
52
47
|
await this.indexer[Symbol.asyncDispose]?.();
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
|
-
exports.FilteredIndexer = FilteredIndexer;
|
|
56
50
|
//# sourceMappingURL=filtered-indexer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filtered-indexer.js","sourceRoot":"","sources":["../src/filtered-indexer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filtered-indexer.js","sourceRoot":"","sources":["../src/filtered-indexer.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IAG1B,YACW,OAAwD,EACxD,MAAc;QADd,YAAO,GAAP,OAAO,CAAiD;QACxD,WAAM,GAAN,MAAM,CAAQ;QAJN,aAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAK5C,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;QAElC,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,sBAAsB;gBACtB,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;YAC7D,CAAC;YAED,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACrD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACzB,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACpB,MAAM,GAAG,CAAA;YACX,CAAC;QACH,CAAC;QAED,2EAA2E;QAC3E,wEAAwE;QACxE,4EAA4E;QAC5E,uDAAuD;QAEvD,IAAI,WAAoB,CAAA;QACxB,GAAG,CAAC;YACF,WAAW,GAAG,KAAK,CAAA;YACnB,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBACtB,MAAM,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;oBAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;oBAChB,WAAW,GAAG,IAAI,CAAA;gBACpB,CAAC;YACH,CAAC;QACH,CAAC,QAAQ,WAAW,EAAC;IACvB,CAAC;IAED,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QACzB,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,CAAA;IAC7C,CAAC;CACF","sourcesContent":["import { LexiconDocument, LexiconIndexer } from '@atproto/lex-document'\nimport { Filter } from './filter.js'\n\n/**\n * A lexicon indexer that filters documents based on a provided filter.\n *\n * If a document was filtered out but later requested via `get()`, the filter\n * will be bypassed for that document.\n */\nexport class FilteredIndexer implements LexiconIndexer, AsyncDisposable {\n protected readonly returned = new Set<string>()\n\n constructor(\n readonly indexer: LexiconIndexer & AsyncIterable<LexiconDocument>,\n readonly filter: Filter,\n ) {}\n\n async get(id: string): Promise<LexiconDocument> {\n this.returned.add(id)\n return this.indexer.get(id)\n }\n\n async *[Symbol.asyncIterator]() {\n const returned = new Set<string>()\n\n for await (const doc of this.indexer) {\n if (returned.has(doc.id)) {\n // Should never happen\n throw new Error(`Duplicate lexicon document id: ${doc.id}`)\n }\n\n if (this.returned.has(doc.id) || this.filter(doc.id)) {\n this.returned.add(doc.id)\n returned.add(doc.id)\n yield doc\n }\n }\n\n // When we yield control back to the caller, there may be requests (.get())\n // for documents that were initially ignored (filtered out). We won't be\n // done iterating until every document that may have been requested when the\n // control was yielded to the caller has been returned.\n\n let returnedAny: boolean\n do {\n returnedAny = false\n for (const id of this.returned) {\n if (!returned.has(id)) {\n yield await this.indexer.get(id)\n returned.add(id)\n returnedAny = true\n }\n }\n } while (returnedAny)\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.indexer[Symbol.asyncDispose]?.()\n }\n}\n"]}
|
package/dist/formatter.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Formatter = void 0;
|
|
4
|
-
const prettier_1 = require("prettier");
|
|
1
|
+
import { format as prettierFormat } from 'prettier';
|
|
5
2
|
const DEFAULT_FORMAT_OPTIONS = {
|
|
6
3
|
parser: 'typescript',
|
|
7
4
|
tabWidth: 2,
|
|
@@ -22,11 +19,7 @@ const DEFAULT_BANNER = `/*
|
|
|
22
19
|
* const formatted = await formatter.format(generatedCode)
|
|
23
20
|
* ```
|
|
24
21
|
*/
|
|
25
|
-
class Formatter {
|
|
26
|
-
/** The banner comment to prepend to formatted code. */
|
|
27
|
-
banner;
|
|
28
|
-
/** Prettier options, or `null` if formatting is disabled. */
|
|
29
|
-
prettierOptions;
|
|
22
|
+
export class Formatter {
|
|
30
23
|
/**
|
|
31
24
|
* Creates a new Formatter instance.
|
|
32
25
|
*
|
|
@@ -50,10 +43,9 @@ class Formatter {
|
|
|
50
43
|
async format(code) {
|
|
51
44
|
const bannerPadding = this.banner && !this.banner.endsWith('\n') ? '\n\n' : '';
|
|
52
45
|
const codePretty = this.prettierOptions
|
|
53
|
-
? await (
|
|
46
|
+
? await prettierFormat(code, this.prettierOptions)
|
|
54
47
|
: code;
|
|
55
48
|
return `${this.banner}${bannerPadding}${codePretty}`;
|
|
56
49
|
}
|
|
57
50
|
}
|
|
58
|
-
exports.Formatter = Formatter;
|
|
59
51
|
//# sourceMappingURL=formatter.js.map
|
package/dist/formatter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,MAAM,IAAI,cAAc,EAAE,MAAM,UAAU,CAAA;AAE/E,MAAM,sBAAsB,GAAoB;IAC9C,MAAM,EAAE,YAAY;IACpB,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,KAAK;IACX,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,KAAK;CACrB,CAAA;AAED,MAAM,cAAc,GAAG;;IAEnB,CAAA;AAwBJ;;;;;;;;;GASG;AACH,MAAM,OAAO,SAAS;IAMpB;;;;OAIG;IACH,YAAY,UAA4B,EAAE;QACxC,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,cAAc,CAAA;QAE/C,IAAI,CAAC,eAAe;YAClB,OAAO,EAAE,MAAM,KAAK,IAAI;gBACtB,CAAC,CAAC,sBAAsB;gBACxB,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI,CAAA;IAC/B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,aAAa,GACjB,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe;YACrC,CAAC,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC;YAClD,CAAC,CAAC,IAAI,CAAA;QACR,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,aAAa,GAAG,UAAU,EAAE,CAAA;IACtD,CAAC;CACF","sourcesContent":["import { Options as PrettierOptions, format as prettierFormat } from 'prettier'\n\nconst DEFAULT_FORMAT_OPTIONS: PrettierOptions = {\n parser: 'typescript',\n tabWidth: 2,\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n}\n\nconst DEFAULT_BANNER = `/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */`\n\n/**\n * Options for configuring the code formatter.\n */\nexport type FormatterOptions = {\n /**\n * Whether to format the generated code with Prettier.\n *\n * - `false`: No formatting (default)\n * - `true`: Format with default Prettier options\n * - `PrettierOptions`: Format with custom Prettier configuration\n *\n * @default false\n */\n pretty?: boolean | PrettierOptions\n /**\n * A banner comment to prepend to each generated file.\n *\n * @default '/* THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT. *\\/'\n */\n banner?: string\n}\n\n/**\n * Formats generated TypeScript code with optional Prettier formatting\n * and banner comments.\n *\n * @example\n * ```ts\n * const formatter = new Formatter({ pretty: true })\n * const formatted = await formatter.format(generatedCode)\n * ```\n */\nexport class Formatter {\n /** The banner comment to prepend to formatted code. */\n readonly banner: string\n /** Prettier options, or `null` if formatting is disabled. */\n readonly prettierOptions: PrettierOptions | null\n\n /**\n * Creates a new Formatter instance.\n *\n * @param options - Formatting configuration options\n */\n constructor(options: FormatterOptions = {}) {\n this.banner = options?.banner ?? DEFAULT_BANNER\n\n this.prettierOptions =\n options?.pretty === true\n ? DEFAULT_FORMAT_OPTIONS\n : options?.pretty || null\n }\n\n /**\n * Formats the given code string.\n *\n * Applies Prettier formatting if enabled, and prepends the banner comment.\n *\n * @param code - The TypeScript code to format\n * @returns The formatted code with banner\n */\n async format(code: string) {\n const bannerPadding =\n this.banner && !this.banner.endsWith('\\n') ? '\\n\\n' : ''\n const codePretty = this.prettierOptions\n ? await prettierFormat(code, this.prettierOptions)\n : code\n return `${this.banner}${bannerPadding}${codePretty}`\n }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.build = build;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
1
|
// Must be first
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import './polyfill.js';
|
|
3
|
+
import { LexBuilder, } from './lex-builder.js';
|
|
4
|
+
export * from './lex-builder.js';
|
|
5
|
+
export * from './lex-def-builder.js';
|
|
6
|
+
export * from './lexicon-directory-indexer.js';
|
|
11
7
|
/**
|
|
12
8
|
* Builds TypeScript schemas from Lexicon documents.
|
|
13
9
|
*
|
|
@@ -31,8 +27,8 @@ tslib_1.__exportStar(require("./lexicon-directory-indexer.js"), exports);
|
|
|
31
27
|
* })
|
|
32
28
|
* ```
|
|
33
29
|
*/
|
|
34
|
-
async function build(options) {
|
|
35
|
-
const builder = new
|
|
30
|
+
export async function build(options) {
|
|
31
|
+
const builder = new LexBuilder(options);
|
|
36
32
|
await builder.load(options);
|
|
37
33
|
await builder.save(options);
|
|
38
34
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,OAAO,eAAe,CAAA;AAEtB,OAAO,EACL,UAAU,GAIX,MAAM,kBAAkB,CAAA;AAEzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,gCAAgC,CAAA;AAgB9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAA8B;IACxD,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;IACvC,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3B,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AAC7B,CAAC","sourcesContent":["// Must be first\nimport './polyfill.js'\n\nimport {\n LexBuilder,\n LexBuilderLoadOptions,\n LexBuilderOptions,\n LexBuilderSaveOptions,\n} from './lex-builder.js'\n\nexport * from './lex-builder.js'\nexport * from './lex-def-builder.js'\nexport * from './lexicon-directory-indexer.js'\n\n/**\n * Combined options for building a TypeScript project from Lexicon documents.\n *\n * This type merges all configuration options needed for the complete build\n * process, including builder configuration, loading options, and save options.\n *\n * @see {@link LexBuilderOptions} for builder configuration\n * @see {@link LexBuilderLoadOptions} for lexicon loading options\n * @see {@link LexBuilderSaveOptions} for output save options\n */\nexport type TsProjectBuildOptions = LexBuilderOptions &\n LexBuilderLoadOptions &\n LexBuilderSaveOptions\n\n/**\n * Builds TypeScript schemas from Lexicon documents.\n *\n * This is the main entry point for programmatic usage of the lex-builder\n * package. It creates a new {@link LexBuilder} instance, loads lexicon\n * documents from the specified directory, and saves the generated TypeScript\n * files to the output directory.\n *\n * @param options - Combined build options including source directory, output\n * directory, and generation settings\n *\n * @example\n * ```ts\n * import { build } from '@atproto/lex-builder'\n *\n * await build({\n * lexicons: './lexicons',\n * out: './src/generated',\n * pretty: true,\n * clear: true,\n * })\n * ```\n */\nexport async function build(options: TsProjectBuildOptions) {\n const builder = new LexBuilder(options)\n await builder.load(options)\n await builder.save(options)\n}\n"]}
|
package/dist/lex-builder.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const lex_def_builder_js_1 = require("./lex-def-builder.js");
|
|
13
|
-
const lexicon_directory_indexer_js_1 = require("./lexicon-directory-indexer.js");
|
|
14
|
-
const ts_lang_js_1 = require("./ts-lang.js");
|
|
1
|
+
import { __addDisposableResource, __disposeResources } from "tslib";
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { mkdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { IndentationText, Project } from 'ts-morph';
|
|
6
|
+
import { buildFilter } from './filter.js';
|
|
7
|
+
import { FilteredIndexer } from './filtered-indexer.js';
|
|
8
|
+
import { Formatter } from './formatter.js';
|
|
9
|
+
import { LexDefBuilder } from './lex-def-builder.js';
|
|
10
|
+
import { LexiconDirectoryIndexer, } from './lexicon-directory-indexer.js';
|
|
11
|
+
import { asNamespaceExport } from './ts-lang.js';
|
|
15
12
|
/**
|
|
16
13
|
* Main builder class for generating TypeScript schemas from Lexicon documents.
|
|
17
14
|
*
|
|
@@ -32,12 +29,11 @@ const ts_lang_js_1 = require("./ts-lang.js");
|
|
|
32
29
|
* await builder.save({ out: './src/generated', clear: true })
|
|
33
30
|
* ```
|
|
34
31
|
*/
|
|
35
|
-
class LexBuilder {
|
|
36
|
-
options;
|
|
32
|
+
export class LexBuilder {
|
|
37
33
|
#imported = new Set();
|
|
38
|
-
#project = new
|
|
34
|
+
#project = new Project({
|
|
39
35
|
useInMemoryFileSystem: true,
|
|
40
|
-
manipulationSettings: { indentationText:
|
|
36
|
+
manipulationSettings: { indentationText: IndentationText.TwoSpaces },
|
|
41
37
|
});
|
|
42
38
|
constructor(options = {}) {
|
|
43
39
|
this.options = options;
|
|
@@ -51,7 +47,7 @@ class LexBuilder {
|
|
|
51
47
|
async load(options) {
|
|
52
48
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
53
49
|
try {
|
|
54
|
-
const indexer =
|
|
50
|
+
const indexer = __addDisposableResource(env_1, new FilteredIndexer(new LexiconDirectoryIndexer(options), buildFilter(options)), true);
|
|
55
51
|
for await (const doc of indexer) {
|
|
56
52
|
if (!this.#imported.has(doc.id)) {
|
|
57
53
|
this.#imported.add(doc.id);
|
|
@@ -68,27 +64,27 @@ class LexBuilder {
|
|
|
68
64
|
env_1.hasError = true;
|
|
69
65
|
}
|
|
70
66
|
finally {
|
|
71
|
-
const result_1 =
|
|
67
|
+
const result_1 = __disposeResources(env_1);
|
|
72
68
|
if (result_1)
|
|
73
69
|
await result_1;
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
async save(options) {
|
|
77
73
|
const files = this.#project.getSourceFiles();
|
|
78
|
-
const destination =
|
|
74
|
+
const destination = resolve(options.out);
|
|
79
75
|
if (options.clear) {
|
|
80
|
-
await
|
|
76
|
+
await rm(destination, { recursive: true, force: true });
|
|
81
77
|
}
|
|
82
78
|
else if (!options.override) {
|
|
83
|
-
await Promise.all(files.map(async (f) => assertNotFileExists(
|
|
79
|
+
await Promise.all(files.map(async (f) => assertNotFileExists(join(destination, f.getFilePath()))));
|
|
84
80
|
}
|
|
85
|
-
const formatter = new
|
|
81
|
+
const formatter = new Formatter(options);
|
|
86
82
|
await Promise.all(Array.from(files, async (file) => {
|
|
87
|
-
const filePath =
|
|
83
|
+
const filePath = join(destination, file.getFilePath());
|
|
88
84
|
const content = await formatter.format(file.getFullText());
|
|
89
|
-
await
|
|
90
|
-
await
|
|
91
|
-
await
|
|
85
|
+
await mkdir(join(filePath, '..'), { recursive: true });
|
|
86
|
+
await rm(filePath, { recursive: true, force: true });
|
|
87
|
+
await writeFile(filePath, content, 'utf8');
|
|
92
88
|
}));
|
|
93
89
|
}
|
|
94
90
|
createFile(path) {
|
|
@@ -102,12 +98,12 @@ class LexBuilder {
|
|
|
102
98
|
if (this.options.indexFile) {
|
|
103
99
|
const indexFile = this.getFile(`/index${this.fileExt}`);
|
|
104
100
|
const tldNs = namespaces[0];
|
|
105
|
-
(
|
|
101
|
+
assert(tldNs !== 'index', 'The "indexFile" options cannot be used with namespaces using a ".index" tld.');
|
|
106
102
|
const tldNsSpecifier = `./${tldNs}${this.importExt}`;
|
|
107
103
|
if (!indexFile.getExportDeclaration(tldNsSpecifier)) {
|
|
108
104
|
indexFile.addExportDeclaration({
|
|
109
105
|
moduleSpecifier: tldNsSpecifier,
|
|
110
|
-
namespaceExport:
|
|
106
|
+
namespaceExport: asNamespaceExport(tldNs),
|
|
111
107
|
});
|
|
112
108
|
}
|
|
113
109
|
}
|
|
@@ -115,26 +111,26 @@ class LexBuilder {
|
|
|
115
111
|
for (let i = 0; i < namespaces.length - 1; i++) {
|
|
116
112
|
const currentNs = namespaces[i];
|
|
117
113
|
const childNs = namespaces[i + 1];
|
|
118
|
-
const path =
|
|
114
|
+
const path = join('/', ...namespaces.slice(0, i + 1));
|
|
119
115
|
const file = this.getFile(`${path}${this.fileExt}`);
|
|
120
116
|
const childModuleSpecifier = `./${currentNs}/${childNs}${this.importExt}`;
|
|
121
117
|
const dec = file.getExportDeclaration(childModuleSpecifier);
|
|
122
118
|
if (!dec) {
|
|
123
119
|
file.addExportDeclaration({
|
|
124
120
|
moduleSpecifier: childModuleSpecifier,
|
|
125
|
-
namespaceExport:
|
|
121
|
+
namespaceExport: asNamespaceExport(childNs),
|
|
126
122
|
});
|
|
127
123
|
}
|
|
128
124
|
}
|
|
129
125
|
// The child file exports the schemas (as *)
|
|
130
|
-
const path =
|
|
126
|
+
const path = join('/', ...namespaces);
|
|
131
127
|
const file = this.getFile(`${path}${this.fileExt}`);
|
|
132
128
|
file.addExportDeclaration({
|
|
133
129
|
moduleSpecifier: `./${namespaces.at(-1)}.defs${this.importExt}`,
|
|
134
130
|
});
|
|
135
131
|
// @NOTE Individual exports exports from the defs file might conflict with
|
|
136
132
|
// child namespaces. For this reason, we also add a namespace export for the
|
|
137
|
-
// defs (export * as $defs from './xyz.defs'). This is an escape hatch
|
|
133
|
+
// defs (export * as $defs from './xyz.defs.js'). This is an escape hatch
|
|
138
134
|
// allowing to still access the definitions if a hash get shadowed by a
|
|
139
135
|
// child namespace.
|
|
140
136
|
file.addExportDeclaration({
|
|
@@ -143,16 +139,15 @@ class LexBuilder {
|
|
|
143
139
|
});
|
|
144
140
|
}
|
|
145
141
|
async createDefsFile(doc, indexer) {
|
|
146
|
-
const path =
|
|
142
|
+
const path = join('/', ...doc.id.split('.'));
|
|
147
143
|
const file = this.createFile(`${path}.defs${this.fileExt}`);
|
|
148
|
-
const fileBuilder = new
|
|
144
|
+
const fileBuilder = new LexDefBuilder(this.options, file, doc, indexer);
|
|
149
145
|
await fileBuilder.build();
|
|
150
146
|
}
|
|
151
147
|
}
|
|
152
|
-
exports.LexBuilder = LexBuilder;
|
|
153
148
|
async function assertNotFileExists(file) {
|
|
154
149
|
try {
|
|
155
|
-
await
|
|
150
|
+
await stat(file);
|
|
156
151
|
throw new Error(`File already exists: ${file}`);
|
|
157
152
|
}
|
|
158
153
|
catch (err) {
|
package/dist/lex-builder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-builder.js","sourceRoot":"","sources":["../src/lex-builder.ts"],"names":[],"mappings":";;;;AAAA,sEAAgC;AAChC,+CAA6D;AAC7D,yCAAyC;AACzC,uCAAmD;AAEnD,2CAA6D;AAC7D,+DAAuD;AACvD,iDAA4D;AAC5D,6DAA0E;AAC1E,iFAGuC;AACvC,6CAAgD;AAyEhD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAa,UAAU;IAOQ;IANpB,SAAS,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7B,QAAQ,GAAG,IAAI,kBAAO,CAAC;QAC9B,qBAAqB,EAAE,IAAI;QAC3B,oBAAoB,EAAE,EAAE,eAAe,EAAE,0BAAe,CAAC,SAAS,EAAE;KACrE,CAAC,CAAA;IAEF,YAA6B,UAA6B,EAAE;QAA/B,YAAO,GAAP,OAAO,CAAwB;IAAG,CAAC;IAEhE,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,KAAK,CAAA;IACtC,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAA;IACxC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,OAA8B;;;YAC9C,MAAY,OAAO,0CAAG,IAAI,qCAAe,CACvC,IAAI,sDAAuB,CAAC,OAAO,CAAC,EACpC,IAAA,uBAAW,EAAC,OAAO,CAAC,CACrB,OAAA,CAAA;YAED,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAChC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBAC5B,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;gBAC7D,CAAC;gBAED,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;gBACvC,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;YAClC,CAAC;;;;;;;;;;;KACF;IAEM,KAAK,CAAC,IAAI,CAAC,OAA8B;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAA;QAE5C,MAAM,WAAW,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAExC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,IAAA,aAAE,EAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,CAAC;aAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC7B,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CACpB,mBAAmB,CAAC,IAAA,gBAAI,EAAC,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CACxD,CACF,CAAA;QACH,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,wBAAS,CAAC,OAAO,CAAC,CAAA;QAExC,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC/B,MAAM,QAAQ,GAAG,IAAA,gBAAI,EAAC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;YACtD,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;YAC1D,MAAM,IAAA,gBAAK,EAAC,IAAA,gBAAI,EAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACtD,MAAM,IAAA,aAAE,EAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACpD,MAAM,IAAA,oBAAS,EAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC,CAAC,CACH,CAAA;IACH,CAAC;IAEO,UAAU,CAAC,IAAY;QAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAC7C,CAAC;IAEO,OAAO,CAAC,IAAY;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IACnE,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,GAAoB;QACjD,MAAM,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAEpC,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;YAEvD,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAE,CAAA;YAC5B,IAAA,qBAAM,EACJ,KAAK,KAAK,OAAO,EACjB,8EAA8E,CAC/E,CAAA;YACD,MAAM,cAAc,GAAG,KAAK,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;YACpD,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,cAAc,CAAC,EAAE,CAAC;gBACpD,SAAS,CAAC,oBAAoB,CAAC;oBAC7B,eAAe,EAAE,cAAc;oBAC/B,eAAe,EAAE,IAAA,8BAAiB,EAAC,KAAK,CAAC;iBAC1C,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,qCAAqC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;YAC/B,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAEjC,MAAM,IAAI,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;YAEnD,MAAM,oBAAoB,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;YACzE,MAAM,GAAG,GAAG,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;YAC3D,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,IAAI,CAAC,oBAAoB,CAAC;oBACxB,eAAe,EAAE,oBAAoB;oBACrC,eAAe,EAAE,IAAA,8BAAiB,EAAC,OAAO,CAAC;iBAC5C,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,4CAA4C;QAC5C,MAAM,IAAI,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAA;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QAEnD,IAAI,CAAC,oBAAoB,CAAC;YACxB,eAAe,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE;SAChE,CAAC,CAAA;QAEF,0EAA0E;QAC1E,4EAA4E;QAC5E,sEAAsE;QACtE,uEAAuE;QACvE,mBAAmB;QACnB,IAAI,CAAC,oBAAoB,CAAC;YACxB,eAAe,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE;YAC/D,eAAe,EAAE,OAAO;SACzB,CAAC,CAAA;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc,CAC1B,GAAoB,EACpB,OAAuB;QAEvB,MAAM,IAAI,GAAG,IAAA,gBAAI,EAAC,GAAG,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QAE3D,MAAM,WAAW,GAAG,IAAI,kCAAa,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QACvE,MAAM,WAAW,CAAC,KAAK,EAAE,CAAA;IAC3B,CAAC;CACF;AA1ID,gCA0IC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,IAAA,eAAI,EAAC,IAAI,CAAC,CAAA;QAChB,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAA;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAM;QAC1E,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC","sourcesContent":["import assert from 'node:assert'\nimport { mkdir, rm, stat, writeFile } from 'node:fs/promises'\nimport { join, resolve } from 'node:path'\nimport { IndentationText, Project } from 'ts-morph'\nimport { LexiconDocument, LexiconIndexer } from '@atproto/lex-document'\nimport { BuildFilterOptions, buildFilter } from './filter.js'\nimport { FilteredIndexer } from './filtered-indexer.js'\nimport { Formatter, FormatterOptions } from './formatter.js'\nimport { LexDefBuilder, LexDefBuilderOptions } from './lex-def-builder.js'\nimport {\n LexiconDirectoryIndexer,\n LexiconDirectoryIndexerOptions,\n} from './lexicon-directory-indexer.js'\nimport { asNamespaceExport } from './ts-lang.js'\n\n/**\n * Configuration options for the {@link LexBuilder} class.\n *\n * Extends {@link LexDefBuilderOptions} with additional settings for\n * controlling the generated TypeScript project structure.\n *\n * @see {@link LexDefBuilderOptions} for definition generation options\n */\nexport type LexBuilderOptions = LexDefBuilderOptions & {\n /**\n * Whether to generate an index file at the root exporting all top-level\n * namespaces.\n *\n * @note This could theoretically cause name conflicts if a\n * @default false\n */\n indexFile?: boolean\n /**\n * The file extension to use for import specifiers in the generated code.\n *\n * @default '.js'\n */\n importExt?: string\n /**\n * The file extension to use for generated TypeScript files.\n *\n * @default '.ts'\n */\n fileExt?: string\n}\n\n/**\n * Options for loading lexicon documents into the builder.\n *\n * Combines directory indexing options with filtering options to control\n * which lexicon documents are processed.\n *\n * @see {@link LexiconDirectoryIndexerOptions} for directory scanning options\n * @see {@link BuildFilterOptions} for include/exclude filtering\n */\nexport type LexBuilderLoadOptions = LexiconDirectoryIndexerOptions &\n BuildFilterOptions\n\n/**\n * Options for saving generated TypeScript files.\n *\n * Combines formatting options with output directory configuration.\n */\nexport type LexBuilderSaveOptions = FormatterOptions & {\n /**\n * The output directory path where generated TypeScript files will be written.\n */\n out: string\n /**\n * Whether to clear the output directory before writing files.\n *\n * When `true`, the entire output directory is deleted before writing new files.\n *\n * @default false\n */\n clear?: boolean\n /**\n * Whether to allow overwriting existing files.\n *\n * When `false`, an error is thrown if any output file already exists.\n *\n * @default false\n */\n override?: boolean\n}\n\n/**\n * Main builder class for generating TypeScript schemas from Lexicon documents.\n *\n * The LexBuilder orchestrates the entire code generation process:\n * 1. Loading and indexing lexicon documents from the filesystem\n * 2. Generating TypeScript type definitions and runtime schemas\n * 3. Creating namespace export trees for convenient imports\n * 4. Saving formatted output files\n *\n * @example\n * ```ts\n * const builder = new LexBuilder({ indexFile: true, pretty: true })\n *\n * // Load lexicons from a directory\n * await builder.load({ lexicons: './lexicons' })\n *\n * // Save generated TypeScript to output directory\n * await builder.save({ out: './src/generated', clear: true })\n * ```\n */\nexport class LexBuilder {\n readonly #imported = new Set<string>()\n readonly #project = new Project({\n useInMemoryFileSystem: true,\n manipulationSettings: { indentationText: IndentationText.TwoSpaces },\n })\n\n constructor(private readonly options: LexBuilderOptions = {}) {}\n\n get fileExt() {\n return this.options.fileExt ?? '.ts'\n }\n\n get importExt() {\n return this.options.importExt ?? '.js'\n }\n\n public async load(options: LexBuilderLoadOptions) {\n await using indexer = new FilteredIndexer(\n new LexiconDirectoryIndexer(options),\n buildFilter(options),\n )\n\n for await (const doc of indexer) {\n if (!this.#imported.has(doc.id)) {\n this.#imported.add(doc.id)\n } else {\n throw new Error(`Duplicate lexicon document id: ${doc.id}`)\n }\n\n await this.createDefsFile(doc, indexer)\n await this.createExportTree(doc)\n }\n }\n\n public async save(options: LexBuilderSaveOptions) {\n const files = this.#project.getSourceFiles()\n\n const destination = resolve(options.out)\n\n if (options.clear) {\n await rm(destination, { recursive: true, force: true })\n } else if (!options.override) {\n await Promise.all(\n files.map(async (f) =>\n assertNotFileExists(join(destination, f.getFilePath())),\n ),\n )\n }\n\n const formatter = new Formatter(options)\n\n await Promise.all(\n Array.from(files, async (file) => {\n const filePath = join(destination, file.getFilePath())\n const content = await formatter.format(file.getFullText())\n await mkdir(join(filePath, '..'), { recursive: true })\n await rm(filePath, { recursive: true, force: true })\n await writeFile(filePath, content, 'utf8')\n }),\n )\n }\n\n private createFile(path: string) {\n return this.#project.createSourceFile(path)\n }\n\n private getFile(path: string) {\n return this.#project.getSourceFile(path) || this.createFile(path)\n }\n\n private async createExportTree(doc: LexiconDocument) {\n const namespaces = doc.id.split('.')\n\n if (this.options.indexFile) {\n const indexFile = this.getFile(`/index${this.fileExt}`)\n\n const tldNs = namespaces[0]!\n assert(\n tldNs !== 'index',\n 'The \"indexFile\" options cannot be used with namespaces using a \".index\" tld.',\n )\n const tldNsSpecifier = `./${tldNs}${this.importExt}`\n if (!indexFile.getExportDeclaration(tldNsSpecifier)) {\n indexFile.addExportDeclaration({\n moduleSpecifier: tldNsSpecifier,\n namespaceExport: asNamespaceExport(tldNs),\n })\n }\n }\n\n // First create the parent namespaces\n for (let i = 0; i < namespaces.length - 1; i++) {\n const currentNs = namespaces[i]\n const childNs = namespaces[i + 1]\n\n const path = join('/', ...namespaces.slice(0, i + 1))\n const file = this.getFile(`${path}${this.fileExt}`)\n\n const childModuleSpecifier = `./${currentNs}/${childNs}${this.importExt}`\n const dec = file.getExportDeclaration(childModuleSpecifier)\n if (!dec) {\n file.addExportDeclaration({\n moduleSpecifier: childModuleSpecifier,\n namespaceExport: asNamespaceExport(childNs),\n })\n }\n }\n\n // The child file exports the schemas (as *)\n const path = join('/', ...namespaces)\n const file = this.getFile(`${path}${this.fileExt}`)\n\n file.addExportDeclaration({\n moduleSpecifier: `./${namespaces.at(-1)}.defs${this.importExt}`,\n })\n\n // @NOTE Individual exports exports from the defs file might conflict with\n // child namespaces. For this reason, we also add a namespace export for the\n // defs (export * as $defs from './xyz.defs'). This is an escape hatch\n // allowing to still access the definitions if a hash get shadowed by a\n // child namespace.\n file.addExportDeclaration({\n moduleSpecifier: `./${namespaces.at(-1)}.defs${this.importExt}`,\n namespaceExport: '$defs',\n })\n }\n\n private async createDefsFile(\n doc: LexiconDocument,\n indexer: LexiconIndexer,\n ): Promise<void> {\n const path = join('/', ...doc.id.split('.'))\n const file = this.createFile(`${path}.defs${this.fileExt}`)\n\n const fileBuilder = new LexDefBuilder(this.options, file, doc, indexer)\n await fileBuilder.build()\n }\n}\n\nasync function assertNotFileExists(file: string): Promise<void> {\n try {\n await stat(file)\n throw new Error(`File already exists: ${file}`)\n } catch (err) {\n if (err instanceof Error && 'code' in err && err.code === 'ENOENT') return\n throw err\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lex-builder.js","sourceRoot":"","sources":["../src/lex-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert'\nimport { mkdir, rm, stat, writeFile } from 'node:fs/promises'\nimport { join, resolve } from 'node:path'\nimport { IndentationText, Project } from 'ts-morph'\nimport { LexiconDocument, LexiconIndexer } from '@atproto/lex-document'\nimport { BuildFilterOptions, buildFilter } from './filter.js'\nimport { FilteredIndexer } from './filtered-indexer.js'\nimport { Formatter, FormatterOptions } from './formatter.js'\nimport { LexDefBuilder, LexDefBuilderOptions } from './lex-def-builder.js'\nimport {\n LexiconDirectoryIndexer,\n LexiconDirectoryIndexerOptions,\n} from './lexicon-directory-indexer.js'\nimport { asNamespaceExport } from './ts-lang.js'\n\n/**\n * Configuration options for the {@link LexBuilder} class.\n *\n * Extends {@link LexDefBuilderOptions} with additional settings for\n * controlling the generated TypeScript project structure.\n *\n * @see {@link LexDefBuilderOptions} for definition generation options\n */\nexport type LexBuilderOptions = LexDefBuilderOptions & {\n /**\n * Whether to generate an index file at the root exporting all top-level\n * namespaces.\n *\n * @note This could theoretically cause name conflicts if a\n * @default false\n */\n indexFile?: boolean\n /**\n * The file extension to use for import specifiers in the generated code.\n *\n * @default '.js'\n */\n importExt?: string\n /**\n * The file extension to use for generated TypeScript files.\n *\n * @default '.ts'\n */\n fileExt?: string\n}\n\n/**\n * Options for loading lexicon documents into the builder.\n *\n * Combines directory indexing options with filtering options to control\n * which lexicon documents are processed.\n *\n * @see {@link LexiconDirectoryIndexerOptions} for directory scanning options\n * @see {@link BuildFilterOptions} for include/exclude filtering\n */\nexport type LexBuilderLoadOptions = LexiconDirectoryIndexerOptions &\n BuildFilterOptions\n\n/**\n * Options for saving generated TypeScript files.\n *\n * Combines formatting options with output directory configuration.\n */\nexport type LexBuilderSaveOptions = FormatterOptions & {\n /**\n * The output directory path where generated TypeScript files will be written.\n */\n out: string\n /**\n * Whether to clear the output directory before writing files.\n *\n * When `true`, the entire output directory is deleted before writing new files.\n *\n * @default false\n */\n clear?: boolean\n /**\n * Whether to allow overwriting existing files.\n *\n * When `false`, an error is thrown if any output file already exists.\n *\n * @default false\n */\n override?: boolean\n}\n\n/**\n * Main builder class for generating TypeScript schemas from Lexicon documents.\n *\n * The LexBuilder orchestrates the entire code generation process:\n * 1. Loading and indexing lexicon documents from the filesystem\n * 2. Generating TypeScript type definitions and runtime schemas\n * 3. Creating namespace export trees for convenient imports\n * 4. Saving formatted output files\n *\n * @example\n * ```ts\n * const builder = new LexBuilder({ indexFile: true, pretty: true })\n *\n * // Load lexicons from a directory\n * await builder.load({ lexicons: './lexicons' })\n *\n * // Save generated TypeScript to output directory\n * await builder.save({ out: './src/generated', clear: true })\n * ```\n */\nexport class LexBuilder {\n readonly #imported = new Set<string>()\n readonly #project = new Project({\n useInMemoryFileSystem: true,\n manipulationSettings: { indentationText: IndentationText.TwoSpaces },\n })\n\n constructor(private readonly options: LexBuilderOptions = {}) {}\n\n get fileExt() {\n return this.options.fileExt ?? '.ts'\n }\n\n get importExt() {\n return this.options.importExt ?? '.js'\n }\n\n public async load(options: LexBuilderLoadOptions) {\n await using indexer = new FilteredIndexer(\n new LexiconDirectoryIndexer(options),\n buildFilter(options),\n )\n\n for await (const doc of indexer) {\n if (!this.#imported.has(doc.id)) {\n this.#imported.add(doc.id)\n } else {\n throw new Error(`Duplicate lexicon document id: ${doc.id}`)\n }\n\n await this.createDefsFile(doc, indexer)\n await this.createExportTree(doc)\n }\n }\n\n public async save(options: LexBuilderSaveOptions) {\n const files = this.#project.getSourceFiles()\n\n const destination = resolve(options.out)\n\n if (options.clear) {\n await rm(destination, { recursive: true, force: true })\n } else if (!options.override) {\n await Promise.all(\n files.map(async (f) =>\n assertNotFileExists(join(destination, f.getFilePath())),\n ),\n )\n }\n\n const formatter = new Formatter(options)\n\n await Promise.all(\n Array.from(files, async (file) => {\n const filePath = join(destination, file.getFilePath())\n const content = await formatter.format(file.getFullText())\n await mkdir(join(filePath, '..'), { recursive: true })\n await rm(filePath, { recursive: true, force: true })\n await writeFile(filePath, content, 'utf8')\n }),\n )\n }\n\n private createFile(path: string) {\n return this.#project.createSourceFile(path)\n }\n\n private getFile(path: string) {\n return this.#project.getSourceFile(path) || this.createFile(path)\n }\n\n private async createExportTree(doc: LexiconDocument) {\n const namespaces = doc.id.split('.')\n\n if (this.options.indexFile) {\n const indexFile = this.getFile(`/index${this.fileExt}`)\n\n const tldNs = namespaces[0]!\n assert(\n tldNs !== 'index',\n 'The \"indexFile\" options cannot be used with namespaces using a \".index\" tld.',\n )\n const tldNsSpecifier = `./${tldNs}${this.importExt}`\n if (!indexFile.getExportDeclaration(tldNsSpecifier)) {\n indexFile.addExportDeclaration({\n moduleSpecifier: tldNsSpecifier,\n namespaceExport: asNamespaceExport(tldNs),\n })\n }\n }\n\n // First create the parent namespaces\n for (let i = 0; i < namespaces.length - 1; i++) {\n const currentNs = namespaces[i]\n const childNs = namespaces[i + 1]\n\n const path = join('/', ...namespaces.slice(0, i + 1))\n const file = this.getFile(`${path}${this.fileExt}`)\n\n const childModuleSpecifier = `./${currentNs}/${childNs}${this.importExt}`\n const dec = file.getExportDeclaration(childModuleSpecifier)\n if (!dec) {\n file.addExportDeclaration({\n moduleSpecifier: childModuleSpecifier,\n namespaceExport: asNamespaceExport(childNs),\n })\n }\n }\n\n // The child file exports the schemas (as *)\n const path = join('/', ...namespaces)\n const file = this.getFile(`${path}${this.fileExt}`)\n\n file.addExportDeclaration({\n moduleSpecifier: `./${namespaces.at(-1)}.defs${this.importExt}`,\n })\n\n // @NOTE Individual exports exports from the defs file might conflict with\n // child namespaces. For this reason, we also add a namespace export for the\n // defs (export * as $defs from './xyz.defs.js'). This is an escape hatch\n // allowing to still access the definitions if a hash get shadowed by a\n // child namespace.\n file.addExportDeclaration({\n moduleSpecifier: `./${namespaces.at(-1)}.defs${this.importExt}`,\n namespaceExport: '$defs',\n })\n }\n\n private async createDefsFile(\n doc: LexiconDocument,\n indexer: LexiconIndexer,\n ): Promise<void> {\n const path = join('/', ...doc.id.split('.'))\n const file = this.createFile(`${path}.defs${this.fileExt}`)\n\n const fileBuilder = new LexDefBuilder(this.options, file, doc, indexer)\n await fileBuilder.build()\n }\n}\n\nasync function assertNotFileExists(file: string): Promise<void> {\n try {\n await stat(file)\n throw new Error(`File already exists: ${file}`)\n } catch (err) {\n if (err instanceof Error && 'code' in err && err.code === 'ENOENT') return\n throw err\n }\n}\n"]}
|
package/dist/lex-def-builder.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const lex_schema_1 = require("@atproto/lex-schema");
|
|
6
|
-
const ref_resolver_js_1 = require("./ref-resolver.js");
|
|
7
|
-
const ts_lang_js_1 = require("./ts-lang.js");
|
|
1
|
+
import { VariableDeclarationKind, } from 'ts-morph';
|
|
2
|
+
import { l } from '@atproto/lex-schema';
|
|
3
|
+
import { RefResolver, getPublicIdentifiers, } from './ref-resolver.js';
|
|
4
|
+
import { asNamespaceExport } from './ts-lang.js';
|
|
8
5
|
/**
|
|
9
6
|
* Builds TypeScript type definitions and runtime schemas from a single
|
|
10
7
|
* Lexicon document.
|
|
@@ -18,23 +15,19 @@ const ts_lang_js_1 = require("./ts-lang.js");
|
|
|
18
15
|
* Each lexicon definition type (record, object, query, procedure, etc.)
|
|
19
16
|
* is handled with specialized code generation logic.
|
|
20
17
|
*/
|
|
21
|
-
class LexDefBuilder {
|
|
22
|
-
options;
|
|
23
|
-
file;
|
|
24
|
-
doc;
|
|
25
|
-
refResolver;
|
|
18
|
+
export class LexDefBuilder {
|
|
26
19
|
constructor(options, file, doc, indexer) {
|
|
27
20
|
this.options = options;
|
|
28
21
|
this.file = file;
|
|
29
22
|
this.doc = doc;
|
|
30
|
-
this.refResolver = new
|
|
23
|
+
this.refResolver = new RefResolver(doc, file, indexer, options);
|
|
31
24
|
}
|
|
32
25
|
pure(code) {
|
|
33
26
|
return this.options.pureAnnotations ? markPure(code) : code;
|
|
34
27
|
}
|
|
35
28
|
async build() {
|
|
36
29
|
this.file.addVariableStatement({
|
|
37
|
-
declarationKind:
|
|
30
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
38
31
|
declarations: [
|
|
39
32
|
{ name: '$nsid', initializer: JSON.stringify(this.doc.id) },
|
|
40
33
|
],
|
|
@@ -58,7 +51,7 @@ class LexDefBuilder {
|
|
|
58
51
|
if (entries.length) {
|
|
59
52
|
this.file.addVariableStatement({
|
|
60
53
|
isExported: true,
|
|
61
|
-
declarationKind:
|
|
54
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
62
55
|
declarations: entries.map(([name, initializer]) => ({
|
|
63
56
|
name,
|
|
64
57
|
initializer,
|
|
@@ -229,7 +222,7 @@ class LexDefBuilder {
|
|
|
229
222
|
const key = JSON.stringify(def.key ?? 'any');
|
|
230
223
|
const objectSchema = await this.compileObjectSchema(def.record);
|
|
231
224
|
const properties = await this.compilePropertiesTypes(def.record);
|
|
232
|
-
properties.unshift(`$type: ${JSON.stringify(
|
|
225
|
+
properties.unshift(`$type: ${JSON.stringify(l.$type(this.doc.id, hash))}`);
|
|
233
226
|
await this.addSchema(hash, def, {
|
|
234
227
|
type: `{ ${properties.join(';')} }`,
|
|
235
228
|
schema: (ref) => this.pure(`l.record<${key}, ${ref.typeName}>(${key}, $nsid, ${objectSchema})`),
|
|
@@ -240,7 +233,7 @@ class LexDefBuilder {
|
|
|
240
233
|
async addObject(hash, def) {
|
|
241
234
|
const objectSchema = await this.compileObjectSchema(def);
|
|
242
235
|
const properties = await this.compilePropertiesTypes(def);
|
|
243
|
-
properties.unshift(`$type?: ${JSON.stringify(
|
|
236
|
+
properties.unshift(`$type?: ${JSON.stringify(l.$type(this.doc.id, hash))}`);
|
|
244
237
|
await this.addSchema(hash, def, {
|
|
245
238
|
type: `{ ${properties.join(';')} }`,
|
|
246
239
|
schema: (ref) => this.pure(`l.typedObject<${ref.typeName}>($nsid, ${JSON.stringify(hash)}, ${objectSchema})`),
|
|
@@ -251,7 +244,7 @@ class LexDefBuilder {
|
|
|
251
244
|
async addToken(hash, def) {
|
|
252
245
|
await this.addSchema(hash, def, {
|
|
253
246
|
schema: this.pure(`l.token($nsid, ${JSON.stringify(hash)})`),
|
|
254
|
-
type: JSON.stringify(
|
|
247
|
+
type: JSON.stringify(l.$type(this.doc.id, hash)),
|
|
255
248
|
validationUtils: true,
|
|
256
249
|
});
|
|
257
250
|
}
|
|
@@ -274,7 +267,7 @@ class LexDefBuilder {
|
|
|
274
267
|
}
|
|
275
268
|
async addSchema(hash, def, { type, schema, objectUtils, validationUtils, }) {
|
|
276
269
|
const ref = await this.refResolver.resolveLocal(hash);
|
|
277
|
-
const pub =
|
|
270
|
+
const pub = getPublicIdentifiers(hash);
|
|
278
271
|
if (type) {
|
|
279
272
|
this.file.addTypeAlias({
|
|
280
273
|
name: ref.typeName,
|
|
@@ -288,14 +281,14 @@ class LexDefBuilder {
|
|
|
288
281
|
name: ref.typeName,
|
|
289
282
|
alias: ref.typeName === pub.typeName
|
|
290
283
|
? undefined
|
|
291
|
-
:
|
|
284
|
+
: asNamespaceExport(pub.typeName),
|
|
292
285
|
},
|
|
293
286
|
],
|
|
294
287
|
});
|
|
295
288
|
}
|
|
296
289
|
if (schema) {
|
|
297
290
|
this.file.addVariableStatement({
|
|
298
|
-
declarationKind:
|
|
291
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
299
292
|
declarations: [
|
|
300
293
|
{
|
|
301
294
|
name: ref.varName,
|
|
@@ -310,7 +303,7 @@ class LexDefBuilder {
|
|
|
310
303
|
name: ref.varName,
|
|
311
304
|
alias: ref.varName === pub.varName
|
|
312
305
|
? undefined
|
|
313
|
-
:
|
|
306
|
+
: asNamespaceExport(pub.varName),
|
|
314
307
|
},
|
|
315
308
|
],
|
|
316
309
|
});
|
|
@@ -496,7 +489,7 @@ class LexDefBuilder {
|
|
|
496
489
|
return this.pure(`l.withDefault(${schema}, ${JSON.stringify(defaultValue)})`);
|
|
497
490
|
}
|
|
498
491
|
async compileBooleanSchema(def) {
|
|
499
|
-
const schema =
|
|
492
|
+
const schema = l.boolean();
|
|
500
493
|
if (def.default !== undefined) {
|
|
501
494
|
schema.check(def.default);
|
|
502
495
|
}
|
|
@@ -510,7 +503,7 @@ class LexDefBuilder {
|
|
|
510
503
|
return 'boolean';
|
|
511
504
|
}
|
|
512
505
|
async compileIntegerSchema(def) {
|
|
513
|
-
const schema =
|
|
506
|
+
const schema = l.integer(def);
|
|
514
507
|
if (hasConst(def)) {
|
|
515
508
|
schema.check(def.const);
|
|
516
509
|
}
|
|
@@ -539,7 +532,7 @@ class LexDefBuilder {
|
|
|
539
532
|
return 'number';
|
|
540
533
|
}
|
|
541
534
|
async compileStringSchema(def) {
|
|
542
|
-
const schema =
|
|
535
|
+
const schema = l.string(def);
|
|
543
536
|
if (hasConst(def)) {
|
|
544
537
|
schema.check(def.const);
|
|
545
538
|
}
|
|
@@ -698,7 +691,6 @@ class LexDefBuilder {
|
|
|
698
691
|
return def.enum.map((v) => JSON.stringify(v)).join(' | ') || 'never';
|
|
699
692
|
}
|
|
700
693
|
}
|
|
701
|
-
exports.LexDefBuilder = LexDefBuilder;
|
|
702
694
|
function parseDescription(description) {
|
|
703
695
|
if (/deprecated/i.test(description)) {
|
|
704
696
|
const deprecationMatch = description.match(/(\s*deprecated\s*(?:--?|:)?\s*([^-]*)(?:-+)?)/i);
|