Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate from angular selectors. ENG-7482 #1653

Merged
merged 7 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@babel/plugin-transform-react-jsx": "^7.13.12",
"@babel/preset-typescript": "^7.13.0",
"@builder.io/sdk": "^2.1.1",
"@danielx/hera": "^0.8.16",
"astring": "^1.8.6",
"csstype": "^3.0.4",
"fp-ts": "^2.11.10",
Expand Down
16 changes: 16 additions & 0 deletions packages/core/src/__tests__/angular.selector.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { parse } from '../../src/generators/angular/selector-parser.js';

describe('Angular selectors', () => {
test('should parse gnarly selectors', () => {
expect(parse('ccc.c1#wat[co].c2[counter="cool"]#wat[x=\'y\'].c3')).toEqual({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are support for the :not pseudo-class and multiple selectors (e.g. foo, [bar]) in scope? Both are supported by Angular: https://angular.dev/guide/components/selectors#types-of-selectors

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I should add parsing for those. They don't affect the code generation but will need to be accounted for in the parsing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok great! Only other thing I can think of here is to add a test for those cases for parsing.

tagName: 'ccc',
id: 'wat',
classes: ['c1', 'c2', 'c3'],
attributes: {
co: undefined,
counter: '"cool"',
x: "'y'",
},
});
});
});
51 changes: 46 additions & 5 deletions packages/core/src/generators/angular/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import {
ToAngularOptions,
} from './types';

import { parse } from './selector-parser';

const { types } = babel;

const mappers: {
Expand Down Expand Up @@ -427,10 +429,49 @@ export const blockToAngular = ({

str += `</ng-container>`;
} else {
const elSelector = childComponents.find((impName) => impName === json.name)
? kebabCase(json.name)
: json.name;
str += `<${elSelector} `;
let tagName,
id,
classes = [],
attributes;

const isComponent = childComponents.find((impName) => impName === json.name);

if (isComponent) {
const selector = json.meta.selector || blockOptions?.selector;
if (selector) {
try {
({ tagName, id, classes, attributes } = parse(selector));
} catch {
tagName = kebabCase(json.name);
}
} else {
tagName = kebabCase(json.name);
}
} else {
tagName = json.name;
}

str += `<${tagName} `;

if (id) {
str += `#${id} `;
}

// TODO: merge with existing classes/bindings
if (classes.length) {
str += `class="${classes.join(' ')}" `;
}

// TODO: Merge with existing properties
liamdebeasi marked this conversation as resolved.
Show resolved Hide resolved
if (attributes) {
Object.entries(attributes).forEach(([key, value]) => {
if (value) {
str += `${key}=${value} `;
} else {
str += `${key} `;
}
});
}

for (const key in json.properties) {
if (key.startsWith('$')) {
Expand Down Expand Up @@ -516,7 +557,7 @@ export const blockToAngular = ({
.join('\n');
}

str += `</${elSelector}>`;
str += `</${tagName}>`;
}
return str;
};
Expand Down
67 changes: 67 additions & 0 deletions packages/core/src/generators/angular/selector-parser.hera
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Parser for angular selectors
# https://angular.dev/guide/components/selectors#types-of-selectors
liamdebeasi marked this conversation as resolved.
Show resolved Hide resolved
#
# to build:
# ./node_modules/.bin/hera --libPath @danielx/hera/dist/machine.js < src/generators/angular/selector-parser.hera > src/generators/angular/selector-parser.js
Selector
TagName?:tagName Part*:parts ->
const firstId = parts.find(([key]) => key === "id");
let id;
if (firstId) {
id = firstId[1];
}

return {
tagName,
id,
classes: parts.filter(([key]) => key === "class").map(([, value]) => value),
attributes: Object.fromEntries(parts.filter(([key]) => key !== "id" && key !== "class"))
}

TagName
Identifier

Part
Attribute
Class
Id

Attribute
"[" __ AttributeName __ "=" __ AttributeValue __ "]" ->
return [$3, $7]
"[" __ AttributeName __ "]" ->
return [$3]

AttributeValue
$DoubleQuotedString
$SingleQuotedString

DoubleQuotedString
"\"" /(?:\\.|[^"])*/ "\""

SingleQuotedString
"'" /(?:\\.|[^'])*/ "'"

Class
"." Identifier ->
return ["class", $2]

Id
"#" Identifier ->
return ["id", $2]

EscapeSequence
"'"
"\""
"\\"
/./ ->
return "\\" + $0

Identifier
/[_a-zA-Z][_a-zA-Z0-9-]*/ -> $0

AttributeName
Identifier

__
/[ \t]*/
Loading
Loading