-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
235 additions
and
137 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,157 @@ | ||
export type BEMBlock< | ||
TName extends string, | ||
TElements extends Record<string, string | never>, | ||
TModifiers extends string | undefined | ||
> = { | ||
name: TName | ||
elements: TElements | ||
modifiers: TModifiers | ||
import { parseBEM } from 'bem-neon' | ||
import ts, { factory } from 'typescript' | ||
import { pascalCase } from 'pascal-case' | ||
import { paramCase } from 'param-case' | ||
import { EOL } from 'node:os' | ||
|
||
export type ParseOptions = {} | ||
|
||
export class BEMBlock { | ||
#block: ReturnType<typeof parseBEM> | ||
|
||
public constructor(bem: string, _options: ParseOptions = {}) { | ||
this.#block = parseBEM(bem) | ||
} | ||
|
||
public get name() { | ||
return paramCase(this.#block.name) | ||
} | ||
|
||
public set name(value) { | ||
this.#block.name = value | ||
} | ||
|
||
public get elements() { | ||
return structuredClone(this.#block.elements) | ||
} | ||
|
||
public set elements(value) { | ||
this.#block.elements = value | ||
} | ||
|
||
public get modifiers() { | ||
return structuredClone(this.#block.modifiers) | ||
} | ||
|
||
public set modifiers(value) { | ||
this.#block.modifiers = value | ||
} | ||
|
||
/** | ||
* @returns type AST generated by the TypeScript Compiler API. | ||
*/ | ||
public toTypeAST() { | ||
return factory.createTypeAliasDeclaration( | ||
[factory.createToken(ts.SyntaxKind.ExportKeyword)], | ||
factory.createIdentifier(pascalCase(`${this.#block.name}Block`)), | ||
undefined, | ||
factory.createTypeLiteralNode([ | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('name'), | ||
undefined, | ||
factory.createLiteralTypeNode(factory.createStringLiteral(this.name)) | ||
), | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('elements'), | ||
undefined, | ||
factory.createTypeLiteralNode( | ||
this.#block.elements.map((element) => | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier(paramCase(element.name)), | ||
undefined, | ||
element.modifiers.length | ||
? factory.createUnionTypeNode( | ||
element.modifiers.map((modifier) => | ||
factory.createLiteralTypeNode( | ||
factory.createStringLiteral(paramCase(modifier)) | ||
) | ||
) | ||
) | ||
: factory.createKeywordTypeNode( | ||
ts.SyntaxKind.UndefinedKeyword | ||
) | ||
) | ||
) | ||
) | ||
), | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('modifiers'), | ||
undefined, | ||
this.#block.modifiers.length | ||
? factory.createUnionTypeNode( | ||
this.#block.modifiers.map((modifier) => | ||
factory.createLiteralTypeNode( | ||
factory.createStringLiteral(paramCase(modifier)) | ||
) | ||
) | ||
) | ||
: factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) | ||
) | ||
]) | ||
) | ||
} | ||
|
||
/** | ||
* @returns the TypeScript type that represents the BEM structure. | ||
* @example | ||
* fooBlock.toType() | ||
* `export const FooBlock = { | ||
* name: 'foo' | ||
* elements: { | ||
* qux: undefined | ||
* } | ||
* modifiers: 'bar' | 'baz' | ||
* }` | ||
*/ | ||
public toType(printerOptions?: ts.PrinterOptions) { | ||
const printer = ts.createPrinter({ | ||
newLine: ts.NewLineKind.LineFeed, | ||
omitTrailingSemicolon: true, | ||
...printerOptions | ||
}) | ||
|
||
return printer.printNode( | ||
ts.EmitHint.Unspecified, | ||
this.toTypeAST(), | ||
ts.createSourceFile( | ||
'block.ts', | ||
'', | ||
ts.ScriptTarget.Latest, | ||
false, | ||
ts.ScriptKind.TS | ||
) | ||
) | ||
} | ||
|
||
/** | ||
* @param eol defualts to `os.EOL` | ||
* @returns the raw BEM file format string. | ||
* @example fooBlock.toString() // foo[bar,baz]\nqux | ||
*/ | ||
public toString(eol = EOL) { | ||
let result = this.name | ||
if (this.modifiers.length) { | ||
result += `[${this.modifiers.join(',')}]` | ||
} | ||
for (const element of this.elements) { | ||
result += eol + element.name | ||
if (element.modifiers.length) { | ||
result += `[${element.modifiers.join(',')}]` | ||
} | ||
} | ||
result += eol | ||
return result | ||
} | ||
|
||
public valueOf() { | ||
return structuredClone(this.#block) | ||
} | ||
|
||
public toJSON() { | ||
return this.valueOf() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1 @@ | ||
import { parseBEM } from 'bem-neon' | ||
import type { BEMBlock } from './BEMBlock.js' | ||
import ts, { factory } from 'typescript' | ||
|
||
export type ParseOptions = {} | ||
|
||
export function parse( | ||
bem: string, | ||
_options: ParseOptions = {} | ||
): BEMBlock<string, Record<string, string | never>, string | undefined> { | ||
const block = parseBEM(bem) | ||
const blockType = factory.createTypeAliasDeclaration( | ||
[factory.createToken(ts.SyntaxKind.ExportKeyword)], | ||
factory.createIdentifier(`${block.name}Block`), | ||
undefined, | ||
factory.createTypeLiteralNode([ | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('name'), | ||
undefined, | ||
factory.createLiteralTypeNode(factory.createStringLiteral(block.name)) | ||
), | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('elements'), | ||
undefined, | ||
factory.createTypeLiteralNode( | ||
block.elements.map((element) => | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier(element.name), | ||
undefined, | ||
element.modifiers.length | ||
? factory.createUnionTypeNode( | ||
element.modifiers.map((modifier) => | ||
factory.createLiteralTypeNode( | ||
factory.createStringLiteral(modifier) | ||
) | ||
) | ||
) | ||
: factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword) | ||
) | ||
) | ||
) | ||
), | ||
factory.createPropertySignature( | ||
undefined, | ||
factory.createIdentifier('modifiers'), | ||
undefined, | ||
block.modifiers.length | ||
? factory.createUnionTypeNode( | ||
block.modifiers.map((modifier) => | ||
factory.createLiteralTypeNode( | ||
factory.createStringLiteral(modifier) | ||
) | ||
) | ||
) | ||
: factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword) | ||
) | ||
]) | ||
) | ||
|
||
// // Create a source file | ||
// const sourceFile = factory.createSourceFile( | ||
// 'foo.ts', | ||
// '', | ||
// ts.ScriptTarget.Latest, | ||
// false, | ||
// ts.ScriptKind.TS | ||
// ) | ||
|
||
// // // Add the type alias declaration to the source file | ||
// const updatedSourceFile = ts.updateSourceFile(sourceFile, [blockType]) | ||
|
||
// Create a printer to output the TypeScript code | ||
const printer = ts.createPrinter({ | ||
newLine: ts.NewLineKind.LineFeed | ||
}) | ||
|
||
const result = printer.printNode( | ||
ts.EmitHint.Unspecified, | ||
blockType, | ||
ts.createSourceFile( | ||
'block.ts', | ||
'', | ||
ts.ScriptTarget.Latest, | ||
false, | ||
ts.ScriptKind.TS | ||
) | ||
) | ||
|
||
// // Print the type alias declaration | ||
// const result = printer.printNode( | ||
// ts.EmitHint.Unspecified, | ||
// blockType, | ||
// updatedSourceFile | ||
// ) | ||
|
||
// Output the result | ||
console.log(`export ${result}`) | ||
|
||
return { name: block.name, elements: {}, modifiers: '' } | ||
} | ||
export * from './BEMBlock.js' |
Oops, something went wrong.