diff --git a/src/language-js/clean.js b/src/language-js/clean.js
index 9ce72c116..73b4bd196 100644
--- a/src/language-js/clean.js
+++ b/src/language-js/clean.js
@@ -122,6 +122,24 @@ function clean(ast, newObj, parent) {
newObj.value.expression.quasis.forEach(q => delete q.value);
}
+ // CSS template literals in Angular Component decorator
+ if (
+ ast.type === "Decorator" &&
+ ast.expression.type === "CallExpression" &&
+ ast.expression.callee.name === "Component" &&
+ ast.expression.arguments.length === 1 &&
+ ast.expression.arguments[0].properties.some(
+ prop =>
+ prop.key.name === "styles" && prop.value.type === "ArrayExpression"
+ )
+ ) {
+ newObj.expression.arguments[0].properties.forEach(prop => {
+ if (prop.value.type === "ArrayExpression") {
+ prop.value.elements[0].quasis.forEach(q => delete q.value);
+ }
+ });
+ }
+
// styled-components, graphql, markdown
if (
ast.type === "TaggedTemplateExpression" &&
diff --git a/src/language-js/embed.js b/src/language-js/embed.js
index 965162d8f..5cee8f40b 100644
--- a/src/language-js/embed.js
+++ b/src/language-js/embed.js
@@ -18,9 +18,12 @@ function embed(path, print, textToDoc /*, options */) {
switch (node.type) {
case "TemplateLiteral": {
- const isCss = [isStyledJsx, isStyledComponents, isCssProp].some(isIt =>
- isIt(path)
- );
+ const isCss = [
+ isStyledJsx,
+ isStyledComponents,
+ isCssProp,
+ isAngularComponentStyles
+ ].some(isIt => isIt(path));
if (isCss) {
// Get full template literal with expressions replaced by placeholders
@@ -176,6 +179,18 @@ function embed(path, print, textToDoc /*, options */) {
}
}
+function isPropertyWithinAngularComponentDecorator(path, parentIndexToCheck) {
+ const parent = path.getParentNode(parentIndexToCheck);
+ return !!(
+ parent &&
+ parent.type === "Decorator" &&
+ parent.expression &&
+ parent.expression.type === "CallExpression" &&
+ parent.expression.callee &&
+ parent.expression.callee.name === "Component"
+ );
+}
+
function getIndentation(str) {
const firstMatchedIndent = str.match(/^([^\S\n]*)\S/m);
return firstMatchedIndent === null ? "" : firstMatchedIndent[1];
@@ -334,6 +349,41 @@ function isStyledJsx(path) {
);
}
+/**
+ * Angular Components can have:
+ * - Inline HTML template
+ * - Inline CSS styles
+ *
+ * ...which are both within template literals somewhere
+ * inside of the Component decorator factory.
+ *
+ * TODO: Format HTML template once prettier's HTML
+ * formatting is "ready"
+ *
+ * E.g.
+ * @Component({
+ * template: `
...
`,
+ * styles: [`h1 { color: blue; }`]
+ * })
+ */
+function isAngularComponentStyles(path) {
+ const parent = path.getParentNode();
+ const parentParent = path.getParentNode(1);
+ const isWithinArrayValueFromProperty = !!(
+ parent &&
+ (parent.type === "ArrayExpression" && parentParent.type === "Property")
+ );
+ if (
+ isWithinArrayValueFromProperty &&
+ isPropertyWithinAngularComponentDecorator(path, 4)
+ ) {
+ if (parentParent.key && parentParent.key.name === "styles") {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* styled-components template literals
*/
diff --git a/tests/angular_component_examples/__snapshots__/jsfmt.spec.js.snap b/tests/angular_component_examples/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..27efe93ba
--- /dev/null
+++ b/tests/angular_component_examples/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,79 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`test.component.ts 1`] = `
+@Component({
+ selector: 'app-test',
+ template: \`
+ \`,
+ styles: [ \`
+
+ :host {
+ color: red;
+ }
+ div { background: blue
+ }
+\`
+
+]
+})
+class TestComponent {}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+@Component({
+ selector: "app-test",
+ template: \`
+ \`,
+ styles: [
+ \`
+ :host {
+ color: red;
+ }
+ div {
+ background: blue;
+ }
+ \`
+ ]
+})
+class TestComponent {}
+
+`;
+
+exports[`test.component.ts 2`] = `
+@Component({
+ selector: 'app-test',
+ template: \`
+ \`,
+ styles: [ \`
+
+ :host {
+ color: red;
+ }
+ div { background: blue
+ }
+\`
+
+]
+})
+class TestComponent {}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+@Component({
+ selector: "app-test",
+ template: \`
+ \`,
+ styles: [
+ \`
+ :host {
+ color: red;
+ }
+ div {
+ background: blue;
+ }
+ \`,
+ ],
+})
+class TestComponent {}
+
+`;
diff --git a/tests/angular_component_examples/jsfmt.spec.js b/tests/angular_component_examples/jsfmt.spec.js
new file mode 100644
index 000000000..ecbf77cc1
--- /dev/null
+++ b/tests/angular_component_examples/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["typescript"]);
+run_spec(__dirname, ["typescript"], { trailingComma: "es5" });
diff --git a/tests/angular_component_examples/test.component.ts b/tests/angular_component_examples/test.component.ts
new file mode 100644
index 000000000..e83eea6fd
--- /dev/null
+++ b/tests/angular_component_examples/test.component.ts
@@ -0,0 +1,17 @@
+@Component({
+ selector: 'app-test',
+ template: `
+ `,
+ styles: [ `
+
+ :host {
+ color: red;
+ }
+ div { background: blue
+ }
+`
+
+]
+})
+class TestComponent {}