Code Monkey home page Code Monkey logo

prettier-plugin-astro's Introduction

Prettier Plugin for Astro

Official Prettier plugin adding support for formatting .astro files.

Installation

First install Prettier and the plugin:

npm i --save-dev prettier prettier-plugin-astro

Then add the plugin to your Prettier configuration:

// .prettierrc.mjs
/** @type {import("prettier").Config} */
export default {
  plugins: ['prettier-plugin-astro'],
};

Recommended configuration

For optimal compatibility with the different package managers and Prettier plugins, we recommend manually specifying the parser to use for Astro files in your Prettier config as shown in the example below:

// .prettierrc.mjs
/** @type {import("prettier").Config} */
export default {
  plugins: ['prettier-plugin-astro'],
  overrides: [
    {
      files: '*.astro',
      options: {
        parser: 'astro',
      },
    },
  ],
};

To customize formatting behavior, see the Configuration section below.

Formatting with the VS Code Prettier extension directly

Note The Astro VS Code extension uses Prettier and this plugin (prettier-plugin-astro) to format your code. You will only need to install the VS Code Prettier extension separately for formatting if:

  • You are not using Astro's VS Code extension.
  • You want to use features of the Prettier extension that not supported by Astro's own VS Code extension, such as the toolbar panel showing Prettier's status.

Install the VS Code Prettier extension and add the following settings to your VS Code configuration:

{
  "prettier.documentSelectors": ["**/*.astro"],
  "[astro]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

The settings above ensure that VS Code is aware that Prettier can be used for Astro files, and sets Prettier as the default formatter for Astro files.

Reporting issues

When submitting issues about formatting your .astro files in VS Code, please specify which extension you are using to format your files: Astro's own extension or the Prettier extension.

Configuration

Most options from Prettier will work with the plugin and can be set in a configuration file or through CLI flags.

Astro Allow Shorthand

Set if attributes with the same name as their expression should be formatted to the short form automatically (for example, if enabled <element name={name} /> will become simply <element {name} />)

Default CLI Override API Override
false --astro-allow-shorthand <bool> astroAllowShorthand: <bool>

Example .prettierrc.cjs

{
  astroAllowShorthand: false;
}

Contributing

Pull requests of any size and any skill level are welcome, no contribution is too small. Changes to the Astro Prettier Plugin are subject to Astro Governance and should adhere to the Astro Style Guide.

See CONTRIBUTING.md for instructions on how to set up your development environment.

Sponsors

Astro is free, open source software made possible by these wonderful sponsors.

❤️ Sponsor Astro! ❤️

sponsors

prettier-plugin-astro's People

Contributors

alex-grover avatar antonyfaris avatar artemst avatar connorads avatar delucis avatar dependabot[bot] avatar drwpow avatar fugitech avatar github-actions[bot] avatar jasikpark avatar kalmarv avatar lsdsjy avatar lucaseverett avatar mrienstra avatar natemoo-re avatar ota-meshi avatar princesseuh avatar retronav avatar sudocat avatar thecrypticace avatar togami2864 avatar tordans avatar ubmit avatar vasfvitor avatar yamac-kurtulus avatar youkwhd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prettier-plugin-astro's Issues

🐛 BUG: Weird formatting (newlines removed) in scss under some conditions

Describe the Bug

Under some conditions, all newlines are removed from style written in scss. This happens to the whole <style> block (including any other rules) when something like the snippet below is included anywhere in it. The rest of the astro file isn't affected

Steps to Reproduce

  1. Create a file test.astro with the following content:
---
---

<style lang="scss">
  tag {
    $foo: bar;

    .class {
      func($var);
    }
  }
</style>
  1. Format it with [email protected]
  2. The file is transformed to the following:
---
---

<style lang="scss"> tag { $foo: bar; .class { func($var); } }</style>

💡 RFC: Setup prerelease publishing + PR publishing

Background & Motivation

I want to setup https://github.com/snowpackjs/astro/blob/main/.github/workflows/release.yml on the repo + sort out how to actually publish the package as well, but only in a very very beta form.

Proposed Solution

Possible solutions

Just copy https://github.com/snowpackjs/astro/blob/main/.github/workflows/release.yml wholly into the project

Alternatives considered

Risks, downsides, and/or tradeoffs

Open Questions

I guess @snowpackjs/maintainers-core has the perms to setup the npm package?

Detailed Design

No response

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

🐛 BUG: Formatter not working as expected

Describe the Bug

The parser is outputting a weird error code that is incomprehensible to me. Also the code on which this error is occurring works fine in the Astro compiler.

Here's the code on which this is failing:

<aside>
    {tags && tags.length > 0 && (
      <p>
      <span>Tags:&nbsp;</span>
        {tags.map((tag, i) => (
          <a href={`/tags/${tag}`}><code>#{tag}</code></a>
          <span>{i < tags.length - 1 && ', '}</span>
        ))}
      </p>
    )}
    {categories && categories.length > 0 && (
      <p>
      <span>Categories:&nbsp;</span>
        {categories.map((category, i) => (
          <a href={`/categories/${category}`}><code>{category}</code></a>
          <span>{i < categories.length - 1 && ', '}</span>
        ))}
        </p>
    )}
  </aside>

Steps to Reproduce

  1. npm init astro using template minimal
  2. Put the code in any Astro file
  3. Run prettier with the Astro plugin on the file
  4. Error! Describe what went wrong (and what was expected instead)...
    Outputs a stack trace from which I can't conclude what went wrong. It was expected that the code is formatted correctly.

Here's the stack trace:

[error] src/layouts/PostLayout.astro: ParseError: Expected =
[error]     at error (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/utils/error.js:46:15)
[error]     at Parser.error (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/parse/index.js:97:30)
[error]     at Parser.acorn_error (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/parse/index.js:91:10)
[error]     at read_expression (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/parse/read/expression.js:225:12)
[error]     at mustache (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/parse/state/mustache.js:316:54)
[error]     at new Parser (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/parse/index.js:57:15)
[error]     at Object.parse (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/@[email protected]/node_modules/@astrojs/parser/dist/parse/index.js:187:18)
[error]     at Object.parse (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/[email protected]/node_modules/prettier-plugin-astro/dist/index.js:14:32)
[error]     at Object.parse$d [as parse] (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/[email protected]/node_modules/prettier/index.js:12975:19)
[error]     at coreFormat (/home/pranav/Programming/Projects/personal-website/node_modules/.pnpm/[email protected]/node_modules/prettier/index.js:14525:16)

🐛 BUG: Shorthand attributes passed to components are turned into regular strings when `astroAllowShortHand` is false.

Describe the Bug

Prettier changes <Component {attribute} /> to <Component attribute="attribute"> when shorthand attributes are not allowed whereas it should actually be <Component attribute={attribute}>

Steps to Reproduce

  1. npm init astro using template Vue (template does not matter probably)
  2. install prettier plugin npm i -D prettier-plugin-astro
  3. Configure prettier:
//prettierrc.json:
{
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": false,
  "singleQuote": true,
  "astroAllowShorthand": true,
  "printWidth": 120,
  "plugins": [
    "./node_modules/prettier-plugin-astro"
  ],
  "overrides": [
    {
      "files": "*.astro",
      "options": {
        "parser": "astro"
      }
    }
  ]
}
  1. Setup vscode (probably unrelated)
//settings.json
{
  "prettier.documentSelectors": ["**/*.astro"],
  "prettier.configPath": "./.prettierrc.json"
}

  1. Create an astro component, for ex. a layout:
//Layout.astro:
---
import HeadLayout from './HeadLayout.astro'
const { title } = Astro.props
const { metaDesc } = Astro.props
---
const 
<HeadLayout {title} {metaDesc} /> 
  1. Save file in vscode or run prettier. And the file becomes
//Layout.astro:
---
import HeadLayout from './HeadLayout.astro'
const { title } = Astro.props
const { metaDesc } = Astro.props
---
const 
<HeadLayout title="title" metaDesc="metaDesc" /> 

🐛 BUG: Prettier hangs when importing from 'astro/components'

Describe the Bug

The formatting process hangs without any error message if I import Code or Debug from 'astro/components'.

code.astro:

---
import { Code } from 'astro/components';
---
<h1>Test
</h1>

But it works if I import Markdown.

markdown.astro:

---
import { Markdown } from 'astro/components';
---
<h1>Test
</h1>

Steps to Reproduce

  1. npx prettier code.astro
  2. Terminal hangs
  3. npx prettier markdown.astro
  4. Terminal shows you correctly formatted code

I am using

  • astro 1.0.0-beta.69
  • prettier 2.7.1
  • prettier-plugin-astro 0.1.1
  • node 16.15.1
  • npm 8.11.0

🐛 BUG: Shorthand attr with JSX in conditional expression

Describe the Bug

Using a shorthand attribute with JSX in a conditional expression will result in an error.

---
const content = {};
const enable = true
---

{enable && <MyHeader {content} />}

https://stackblitz.com/edit/github-prettier-plugin-astro-demo-klsxqv?file=src%2Fpages%2Findex.astro

No error will occur if the shorthand is not used.

---
const content = {};
const enable = true
---

{enable && <MyHeader content={content} />}

Steps to Reproduce

I made it possible to check with stackblitz.
In the demo, if there is no error, the formatted code will be displayed.

https://stackblitz.com/edit/github-prettier-plugin-astro-demo-klsxqv?file=src%2Fpages%2Findex.astro

🐛 BUG: Inconsistent formatting between frontmatter and component code

Describe the Bug

Using v0.1.0-next.4 I get this formatted code:

---
import ConfigOption from "./ConfigOption.astro";
const DEFAULT_LOCALE = "en";

const [options] = Astro.fetchContent<FetchContentResultBase>(
  "../../nebula-docs/src/data/config-reference.md"
);

const { locale = DEFAULT_LOCALE } = Astro.props;

// Default locale options, for use as fallback if localization is not available
const fallbackOptions = options[DEFAULT_LOCALE].options;
// config options for locale
const configOptions = options[locale] ? options[locale].options : fallbackOptions;
---

<style lang="scss">
  .config-block {
    margin-bottom: 1.5rem;
  }
</style>

{configOptions.map((option, optionIndex) => {
  const fallbackOption = fallbackOptions[optionIndex]

  return (
    <div class="config-block">
      <ConfigOption
        isRequired={option.required}
        description={option.description ?? fallbackOption.description}
        example={option.example ?? fallbackOption.example}
      >
        <h2 id={option.name}>
          {option.name}
          <a
            aria-hidden="true"
            tabindex={-1}
            class="heading-link no-underline"
            href={`#${option.name}`}
          >
            <span class="fas fa-link" />
          </a>
        </h2>
      </ConfigOption>
      {option.suboptions &&
        option.suboptions.map((suboption, subIndex) => {
          const fallbackSuboption = fallbackOption.suboptions[subIndex]

          return (
            <ConfigOption
              isRequired={suboption.required}
              description={suboption.description ?? fallbackSuboption.description}
              example={suboption.example ?? fallbackSuboption.example}
            >
              <h3 id={`${option.name}-${suboption.name}`.replace(new RegExp(", ", "g"), "-")}>
                {option.name}.{suboption.name}
                <a
                  aria-hidden="true"
                  tabindex={-1}
                  class="heading-link no-underline"
                  href={`#${option.name}-${suboption.name}`.replace(new RegExp(", ", "g"), "-")}
                >
                  <span class="fas fa-link" />
                </a>
              </h3>
            </ConfigOption>
          )
        })}
    </div>
  )
})}

The important part is that const fallbackOption = fallbackOptions[optionIndex] gets formatted sans semicolon for some reason? Presumably b/c of different config defaults or a different parser/printer?

It seems like lines of code in a block in the body like the map(() => {}) will be formatted w/o semicolon v.s. the frontmatter will have semicolons by default

Steps to Reproduce

Format the above file; see that nothing changes

🐛 BUG: 0.1.0-next.3 is much slower than 0.0.12 - hangs vscode temporarily

Describe the Bug

To be specific, in the examples below next is 0.1.0-next.3 and latest is 0.0.12.

I'm currently converting a project from Astro 0.20 to Astro 0.25 and also upgrading prettier at the same time to the next version, and I'm running into an issue where formatting on save in VSCode noticeably hangs the editor for several seconds.

On my project:

npm install -D prettier-plugin-astro@latest
npm run format
[...]
Done in 1.44s.

npm install -D prettier-plugin-astro@next
npm run format
[...]
Done in 13.61s.

If I run the yarn prettier -w . several times in a row with the next version, they'll all be around 13 seconds, even though the subsequent runs wouldn't require writing any new changes.

Steps to Reproduce

I tried reproducing this with entire folders from the example projects but unfortunately latest would often fail on files so it wasn't easy to show a complete comparison but for the projects below running yarn prettier -w . was noticeably slower on next :

  • blog
  • blog multiple authors
  • ssr
  • docs

For a VSCode demonstration:

npm init astro -- --template blog-multiple-authors
npm install -D prettier-plugin-astro@next
code .
open src/pages/posts/[...page].astro
make no changes and save

This will result in VSCode hanging for about 6-7 seconds every time I save.

Let me know if you need any details about my VSCode setup (though this is happening at the command line too of course).

🐛 BUG: Expression with leading comments get garbled on formatting

Describe the Bug

Found while running the plugin on the docs, the following:

{
	// For best cross-browser support of sticky or fixed elements, they must not be nested
	// inside elements that hide any overflow axis. The article content hides `overflow-x`,
	// so we must place the mobile TOC here.
	headers && (
		<nav class="mobile-toc">
			<TableOfContents
				client:media="(max-width: 72em)"
				headers={headers}
				labels={{ onThisPage: t('rightSidebar.onThisPage'), overview: t('rightSidebar.overview') }}
				isMobile={true}
			/>
		</nav>
	)
}

gets transformed to:

{For best cross-browser support of sticky or fixed elements, they must not be nested
	/
inside elements that hide any overflow axis. The article content hides `overflow-x`,
	/
so we must place the mobile TOC here.
	h
headers && (
	<nav class="mobile-toc">
		<TableOfContents
			client:media="(max-width: 72em)"
			headers={headers}
			labels={{ onThisPage: t('rightSidebar.onThisPage'), overview: t('rightSidebar.overview') }}
			isMobile={true}
		/>
	</nav>
)}

This kills the component.

What's expected

{
  // For best cross-browser support of sticky or fixed elements, they must not be nested
  // inside elements that hide any overflow axis. The article content hides `overflow-x`,
  // so we must place the mobile TOC here.
  headers && (
    <nav class="mobile-toc">
      <TableOfContents
        client:media="(max-width: 72em)"
        headers={headers}
        labels={{
          onThisPage: t("rightSidebar.onThisPage"),
          overview: t("rightSidebar.overview"),
        }}
        isMobile={true}
      />
    </nav>
  )
}

Steps to Reproduce

  1. Create an expression with a comment in it on the first lines and any content below
  2. Format
  3. Everything is broken!

💡 RFC: Better error reporting for failed parsing/printing

Background & Motivation

I want to get a clearer output from this plugin when it is fed:

---
const {label, name, class, ...htmlProps} = Astro.props;
---

<style lang="scss">
  .label-text {
    font-size: 14px;
    margin-bottom: 8px;
  };

  input {
    display: block;
    box-sizing: border-box;
    height: 48px;
    border: none;
    outline: none;
    border-radius: 4px;
    box-shadow: inset 0 0 0 1px var(--theme-input-border);
    background-color: transparent;
    padding: 16px;
    font-weight: 600;
    font-size: 16px;
    color: var(--color-input-text);
    margin-bottom: 24px;

    &:focus {
      /* Visible in Windows high-contrast themes */
      outline-color: transparent;
      outline-width: 2px;
      outline-style: dotted;
      box-shadow: inset 0 0 0 2px var(--theme-accent);
    }
    &::placeholder {
      color: var(--theme-text-disabled);
      font-weight: 500;
    }

    &:disabled {
      background-color: var(--theme-button-dim);
      color: var(--theme-text-disabled);

      &::placeholder {
        color: var(--theme-text-disabled);
      }
    }

    /*
    * HACK: color-scheme dark wreaks havoc, so we have to try to override the styles
    */
    &:-webkit-autofill {
      box-shadow: 0 0 0 100px var(--theme-bg) inset;
      border: 2px solid var(--theme-input-border);
      padding: 14px; /* Since we are changing from inset box-shadow to border, change padding to prevent layout shifts */
      -webkit-text-fill-color: var(--theme-text) !important;

      &:focus {
        box-shadow: 0 0 0 100px var(--theme-bg) inset;
        border: 2px solid var(--theme-accent);
        padding: 14px;
      }
    }
  }

  
</style>

<label>
  <div class='label-text'>{label}</div>
  <input class="width-100" name={name} {...htmlProps}/>
</label>

in v0.1.0-next.4 I get the output:

[error] src/components/atoms/Input.astro: Error: Unhandled node type "frontmatter"!
[error]     at Object.print (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier-plugin-astro/dist/index.js:448:19)
[error]     at callPluginPrintFunction (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8447:26)
[error]     at mainPrintInternal (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8396:22)
[error]     at mainPrint (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8383:18)
[error]     at /Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8238:27
[error]     at AstPath.each (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8230:11)
[error]     at AstPath.map (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8237:14)
[error]     at Object.print (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier-plugin-astro/dist/index.js:275:48)
[error]     at callPluginPrintFunction (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8447:26)
[error]     at mainPrintInternal (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8396:22)

similar to 0.1.0-next.0 in #165.

The bug here discovered by @antonyfaris is that I'm trying to bind to class which is a reserved name, so parsing fails.

I would prefer there was a clearer error message such as "error: variable cannot be bound to class which is a reserved name" similar to how the TS parser works: prettier playground link from @antonyfaris

Proposed Solution

Possible solutions

Pass through error messages from child parsers of the plugin

Alternatives considered

Risks, downsides, and/or tradeoffs

Open Questions

Detailed Design

No response

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

🐛 BUG: Can't format JSX expression containing multiple root elements

Describe the Bug

The plugin error out when trying to format an expression returning multiple roots, example:

Works

{["Astro"].map((hello) => (
  <>
    <div>Hello {hello}</div>
    <div>Wow {hello}</div>
  </>
))}

Doesn't work

{["Astro"].map((hello) => (
  <div>Hello {hello}</div>
  <div>Wow {hello}</div>
))}

Steps to Reproduce

See code example in description

🐛 BUG: 0.1.0-next.4 changes HTML attribute quotes depending on `singleQuote` option in Prettier

Describe the Bug

With the singleQuote option set to true in .prettierrc, HTML attributes are given single quotes rather than double. This is not the default behaviour of Prettier for HTML. The jsxSingleQuote attribute appears to be the setting that controls this (within JSX files, unsure about other languages).

component.astro before running Prettier:

<h1 class="site-title"></h1>

component.astro after running Prettier:

<h1 class='site-title'></h1>

I've done some digging into the source, and it seems switching line 401 of printer.ts from:

const quote = opts.singleQuote ? "'" : '"';

to

const quote = '"';

is enough to handle it, however it's not something I've thoroughly tested, and I've been unable to get the option-single-quote-true and option-single-quote-false tests re-enabled and working so far.


I did also notice here that the (currently disabled) option-jsx-single-quote-true and option-jsx-single-quote tests aren't testing the right thing. At the moment they're written to check if Prettier will change the quotes inside JSX expressions, whereas when I investigated on a test repo, the jsxSingleQuote option actually controls whether or not attributes on elements in JSX are wrapped in double or single quotes.


Another related issue I noticed is that the attribute quotes issues above doesn't apply to templates nested inside JSX/JSX-like expressions, for example with this code, the class="list" will be modified, but class="list-item" won't be

 <ul class="list">
   {[1, 2, 3].map(value => <li class="list-item">{value}</li>)}
 </ul>

I've not investigated enough to know if this plugin just doesn't run at all on nested expressions or if the issue is specific to the singleQuote rule.

Steps to Reproduce

Part 1:

  1. npm init astro using any template and yarn to install dependencies
  2. yarn add --dev prettier prettier-plugin-astro@next
  3. Create .prettierrc with the following options:
    {
      "singleQuote": true
    }
  4. Run yarn prettier --write .
  5. Error! The value of the <html lang="en"> tag in src/pages/index.astro will now be <html lang='en'>.

Part 2:

  1. Add the following before the </main> tag in index.astro:
    <ul class="list">
      {[1, 2, 3].map(value => <li class="list-item">{value}</li>)}
    </ul>
  2. Run yarn prettier --write .
  3. Error! The value will be changed to:
    <ul class='list'>
      {[1, 2, 3].map(value => <li class="list-item">{value}</li>)}
    </ul>

🐛 BUG: Weird formatting

Describe the Bug

The prettier plugin is generating weird outputs for code blocks that are a bit longer writing in a single line.

<Image src="https://picsum.photos/200/300" alt="A random image" brightness={0.5} />

<Image 
  src="https://picsum.photos/200/300" 
  alt="A random image" 
  brightness={0.5}
/>

<!-- The prettier plugin is formatting the above two code blocks to the below code block -->

<Image src="https://picsum.photos/200/300" alt="A random image" brightness={0.5}
/>

I am not very familiar with how prettier plugins work but I think this plugin first minifies the code and then starts formatting it. I think that's why the multi-line code block is also getting formatted in the same way.

Steps to Reproduce

The prettier plugin should break the single-line code block into multiple lines and keep the multi-line code block as it is.

Incorrect formatting of `<!DOCTYPE html>`

After running the plugin, <!DOCTYPE html> is formatted as <!DOCTYPE html></!DOCTYPE>

Minimal test case:

---

---

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{foo}</title>
  </head>
  <body></body>
</html>

image
This is only reproduced if unempty mustache expression is present.

🐛 BUG: Markdown component indentation broken

Describe the Bug

Astro prettier plugin breaks markdown inside Markdown component when it's nested in another element

Steps to Reproduce

  • Install astro prettier plugin

  • Open attached file

  • Format document

  • Expected: indentation preserved

  • Seen: Second paragraph indented (which then results in <pre> markup)

test.astro.zip

Before format document:

---
---



<div>
  <Markdown>
    Test Test

    Test
  </Markdown>
</div>

After format document:

---
---



<div>
  <Markdown>
    Test Test

        Test
  </Markdown>
</div>

🐛 BUG: Prettier remove attributes in components

Describe the Bug

When I run yarn prettier -w ./src, Prettier remove HTML Attributes name from components :

Before running Prettier :

<BaseLayout title={title}>

After running Prettier :

<BaseLayout {title}>

screen records : https://i.ibb.co/4MqymdK/Enregistrement-de-l-e-cran-2021-10-18-a-11-37-40.gif

Steps to Reproduce

  1. npm init astro using template Starting project
  2. Create a Layout with variable title has props
---
const { title } = Astro.props;
---

<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width" />
      <title>My site Title : {title}</title>
      <!-- ... HEAD CONTENT -->
   </head>
   <body>
      <slot />
   </body>
</html>
  1. Install prettier-plugin-astro
  2. Inside index.astro, call your Layout and pass title has props
---
// Layouts
import BaseLayout from "../layouts/BaseLayout.astro";
let title = "My Astro Site";
---

<BaseLayout title={title}>
   <main>
      <!-- ... BODY_CONTENT -->
   </main>
</BaseLayout>
  1. Run yarn prettier -w ./src
  2. Watch your index.astro

🐛 BUG: Spread properties are not handled correctly

Describe the Bug

When using spread properties on astro components, the formatter ignores the file because of the Error: Unhandled node type "Spread".

Steps to Reproduce

  1. create a const meta = { title: "My Title", lang: "en" }
  2. spread the object on a component as props <BaseLayout {...meta}>Foo</BaseLayout>
  3. Error! Formatter fails with Error: Unhandled node type "Spread".

💡 RFC: Prettier Options

Background & Motivation

This is a list of all the options that are missing from the plugin. We should probably add support for some.

Proposed Solution

Possible solutions

This one I think should be supported. And it may already be supported but I was having problems testing it.

These ones I'm gonna need feedback:

These are the ones that I think shouldn't be supported:

Detailed Design

No response

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

BUG 🐛: `<Footer>` gets rewritten to `<footer>` + all other case insensitive html tag collisions

When an imported component has a tag reference name that collides with one of the standard tag names in HTML - like for instance <Footer /> - the plugin resets it to <footer />. Thus being lowercase then it won't get rendered by astro and the component is missing in the output.

I don't like the clunkiness of having to keep a complete nomenclature in mind to avoid collisions like these.

Also the compiler doesn't complain - how could it be aware? - so the then missing component gets dropped silently.

🐛 BUG: `src/components/atoms/Input.astro: Error: Unhandled node type "Script"!`

Describe the Bug

When formatting with [email protected] I run into this error:

src/components/atoms/Input.astro: Error: Unhandled node type "Script"!

Steps to Reproduce

❯ npx prettier --parser astro src/components/atoms/Input.astro
src/components/atoms/Input.astro[error] src/components/atoms/Input.astro: Error: Unhandled node type "Script"!
[error]     at Object.print (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier-plugin-astro/dist/index.js:706:19)
[error]     at callPluginPrintFunction (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8447:26)
[error]     at mainPrintInternal (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8396:22)
[error]     at mainPrint (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8383:18)
[error]     at AstPath.call (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8205:24)
[error]     at printTopLevelParts (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier-plugin-astro/dist/index.js:445:39)
[error]     at Object.print (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier-plugin-astro/dist/index.js:498:16)
[error]     at callPluginPrintFunction (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8447:26)
[error]     at mainPrintInternal (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8396:22)
[error]     at mainPrint (/Users/calebjasik-defined/Github/defined.net/www/node_modules/prettier/index.js:8383:18)

over Input.astro:

---
const {label, name, class, ...htmlProps} = Astro.props;
---

<style lang="scss">
  .label-text {
    font-size: 14px;
    margin-bottom: 8px;
  };

  input {
    display: block;
    box-sizing: border-box;
    height: 48px;
    border: none;
    outline: none;
    border-radius: 4px;
    box-shadow: inset 0 0 0 1px var(--theme-input-border);
    background-color: transparent;
    padding: 16px;
    font-weight: 600;
    font-size: 16px;
    color: var(--color-input-text);
    margin-bottom: 24px;

    &:focus {
      /* Visible in Windows high-contrast themes */
      outline-color: transparent;
      outline-width: 2px;
      outline-style: dotted;
      box-shadow: inset 0 0 0 2px var(--theme-accent);
    }
    &::placeholder {
      color: var(--theme-text-disabled);
      font-weight: 500;
    }

    &:disabled {
      background-color: var(--theme-button-dim);
      color: var(--theme-text-disabled);

      &::placeholder {
        color: var(--theme-text-disabled);
      }
    }

    /*
    * HACK: color-scheme dark wreaks havoc, so we have to try to override the styles
    */
    &:-webkit-autofill {
      box-shadow: 0 0 0 100px var(--theme-bg) inset;
      border: 2px solid var(--theme-input-border);
      padding: 14px; /* Since we are changing from inset box-shadow to border, change padding to prevent layout shifts */
      -webkit-text-fill-color: var(--theme-text) !important;

      &:focus {
        box-shadow: 0 0 0 100px var(--theme-bg) inset;
        border: 2px solid var(--theme-accent);
        padding: 14px;
      }
    }
  }

  
</style>

<label>
  <div class='label-text'>{label}</div>
  <input class="width-100" name={name} {...htmlProps}/>
</label>

🐛 BUG: Component attributes with long values are not properly splitted in multiple lines

Describe the Bug

My component takes attributes that can have values that are really long, especially arrays. The problem isn't exclusive to arrays but, for other types there's not much solutions outside of putting it on its own line.

Currently, the plugin generate the following formatting which is not really pleasant to read:

<SocialImage
  texts={[{ content: "Princesseuh", attributes: { x: "55", y: "105", "font-size": "70px", fill: "#fefffe" } }, { content: "Introducing Astro: Ship Less JavaScript", attributes: { x: "50", y: "325", "font-size": "40px", fill: "#fefffe" } }]} />

I think it would be better to generate something that looks maybe like this:

<SocialImage
  texts={[
    { content: "Princesseuh", attributes: { x: "55", y: "105", "font-size": "70px", fill: "#fefffe" } },
    { content: "Introducing Astro: Ship Less JavaScript", attributes: { x: "50", y: "325", "font-size": "40px", fill: "#fefffe" } }
  ]} />

Where the arrays elements get splitted at least on their own lines if they're too long (longer than printWidth perhaps? Or close to)

For context, Prettier produce the following result in Javascript:

const arrayTest = [
  {
    content: "Princesseuh",
    attributes: { x: "55", y: "105", "font-size": "70px", fill: "#fefffe" },
  },
  {
    content: "Introducing Astro: Ship Less JavaScript",
    attributes: { x: "50", y: "325", "font-size": "40px", fill: "#fefffe" },
  },
]

Which, in my opinion, is even better than what I'm suggesting, but either would definitely be an improvement over the current result

Current workaround

A way to workaround this at the moment is to manually format it and add a prettier-ignore comment before the array, like this:

<SocialImage
  texts={// prettier-ignore
  [
    { content: "Princesseuh", attributes: { x: "55", y: "105", "font-size": "70px", fill: "#fefffe" } },
    { content: "Introducing Astro: Ship Less JavaScript", attributes: { x: "50", y: "325", "font-size": "40px", fill: "#fefffe" } }
  ]} />

Steps to Reproduce

  1. Create a component with long parameters
  2. Run prettier with the plugin enabled
  3. Lines will be too long

🐛 BUG: Error when formatting HTML comments

Describe the Bug

Hi everyone, thanks for this plugin !

Using the basic index.astro from yarn create astro, this plugin returns an error when using the HTML comments :

<!--
           - You can also use imported framework components directly in your markup!
           -
           - Note: by default, these components are NOT interactive on the client.
           - The `:visible` directive tells Astro to make it interactive.
           -
           - See https://docs.astro.build/core-concepts/component-hydration/  
 -->

I have this error in the prettier log :

["ERROR" - 12:11:59] Error formatting document.
["ERROR" - 12:11:59] Cannot read property 'comments' of undefined
TypeError: Cannot read property 'comments' of undefined
	at addCommentHelper (g:\PROJECTS\Testing\ASTRO\astro_v0.21\node_modules\prettier\index.js:5972:25)
	at Object.addLeadingComment$3 [as addLeadingComment] (g:\PROJECTS\Testing\ASTRO\astro_v0.21\node_modules\prettier\index.js:5981:3)
	at attachCommentsHTML (g:\PROJECTS\Testing\ASTRO\astro_v0.21\node_modules\prettier-plugin-astro\dist\index.js:365:27)
	at Object.print (g:\PROJECTS\Testing\ASTRO\astro_v0.21\node_modules\prettier-plugin-astro\dist\index.js:501:9)

When you remove the comment, everything works fine.

Hope it helps ! Thanks !

Steps to Reproduce

  1. npm create astro using the basic template.
  2. yarn add --dev prettier-plugin-astro to add the plugin.
  3. Format the index.astro page in the src/pages folder
  4. Error! The plugin doesn't work
  5. Remove the comment
  6. The plugin works

🐛 BUG: Incorrect ending tag when formatting without codefence

Describe the Bug

When formating without a codefence it outputs the last tag incorrectly.

Steps to Reproduce

Input:

<style>
  p {
    color: red;
  }
</style>

<p>lorem</p>

Output:

<p>lorem</p>

<style>
  p {
    color: red;
  }
</p>

Expected output:

<p>lorem</p>

<style>
  p {
    color: red;
  }
</style>

If there is a codefence it formats correctly. Like this:

---
---

<style>
  p {
    color: red;
  }
</style>

<p>lorem</p>

🐛 BUG: Strips whitespace between multiple `<style/>` tags

Describe the Bug

When multiple <style/> tags are present in a .astro component, the plugin elides whitespace between then and smashes them together, e.g.

<style>
  /* some scoped styles here */
</style>

<style global>
  /* some global styles here */
</styles>

becomes

<style>
  /* some scoped styles here */
</style><style global>
  /* some global styles here */
</styles>

A quick look at the code suggests a fix might need need something in @astrojs/parser? Or might it be acceptable to something post-hoc here?

case 'styles': {
const subDoc = path.call(print, 'css');
if (!isEmptyDoc(subDoc)) docs.push(normalize(subDoc));
break;

Steps to Reproduce

  1. npm init astro using template
  2. create a .astro component with two or more <style/> elements
  3. observe the behaviour described above

🐛 BUG: No parser could be inferred for file: Author.astro when using pnpm

Describe the Bug

When trying to format / check an .astro file, it does not seem to be able to recognize the formatter when using pnpm.

It does work with yarn.

Steps to Reproduce

  1. npm init astro using template blog
  2. install dependencies with pnpm install
  3. add the plugin pnpm i --save-dev prettier-plugin-astro
  4. try to prettier an astro file pnpm prettier src/components/Author.astro -c
Checking formatting...
src/components/Author.astro[error] No parser could be inferred for file: src/components/Author.astro
All matched files use Prettier code style!

📘 DOC: Document the minimum astro version supported per version

Currently [email protected] fails to format for me. Maybe it need to have a minimum supported astro version documented per version?

Is the astro parser as a dependent of this plugin pinned? Or will it interact w/ the astro version you have open?

the error I was seeing:

src/pages/nebula/[slug].astro[error] src/pages/nebula/[slug].astro: ParseError: (0 , import__2.default) is not a function

[email protected] formats for me w/o error

🐛 BUG: Header and Footer components are being lowercased

Describe the Bug

When I use a component <Header /> or <Footer /> (maybe happens with more components, but I'm not aware of that) they are converted to <header /> and <footer /> HTML elements when they shouldn't

Steps to Reproduce

  1. npm init astro using template minimal
  2. Install prettier and prettier-plugin-astro
  3. Create two components: Header.astro and Footer.astro
  4. Replace your index.astro with this:
---
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
---

<html lang="en">
  <body>
    <Header />

    <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>

    <Footer />
  </body>
</html>
  1. Run prettier -w . (maybe you'll have to run it twice)
  2. You'll see the components <Header /> to be <header /> (and the same with Footer)

💡 RFC: slots should be self-closing

Background & Motivation

slots in astro components should be self-closing:

<slot></slot> ---> <slot />

For now Prettier/prettier-plugin-astro keep transforming <slot /> to <slot></slot> which does not mirror slots examples from the docs.

Risks, downsides, and/or tradeoffs

Opts out of default prettier behavior

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

💡 RFC: Add a CONTRIBUTING.md

Background & Motivation

I want it to be clear how to get setup contributing to the project, and a more thorough guide would be helpful, which would list:

  • How to get setup building and testing
  • How to write tests
  • What the design goals of the formatting are (which is mostly to try to follow the prettier rationale)

Proposed Solution

Possible solutions

Add CONTRIBUTING.md or contributing.md to the repo

Alternatives considered

Just expand the section in the README

Risks, downsides, and/or tradeoffs

Open Questions

Detailed Design

No response

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

🐛 BUG: JSX typed method parameters result in an unwanted ending semicolon.

Describe the Bug

Prettier/this plugin will add an unwanted semicolon at the end of methods with typed parameters.

(Just can't figure out how else to describe this)

Original:

<ul>
	{items.map((item: any) => <li>{item}</li>)}
</ul>

Formatted:

<ul>
	{items.map((item: any) => <li>{item}</li>);}
</ul>

This unwanted semicolon results in ts errors:

'}' expected.ts(1005)
Unexpected token. Did you mean `{'}'}` or `&rbrace;`?ts(1381)

Error is not occurring in v0.4.0 nor when the explicitly set types of the method are omitted.

Steps to Reproduce

  1. https://stackblitz.com/edit/astro-lslult?file=src/pages/index.astro
  2. In console, run npm run format
  3. See the semicolon appear.

🐛 BUG: Footnote inside markdown inside expression breaks after formatting with 0.0.11

Describe the Bug

Input:

{foo ?? (
  <Markdown>
    Test. [^1] 
    
    [^1]: Test.
  </Markdown>
)}

Output:

{foo ?? <Markdown>Test. [^1] [^1]: Test.</Markdown>}

Compare with

Input

<Markdown>
  Test. [^1]

  [^1]: Test.
</Markdown>

Output: unchanged.

The issue is that correct rendering of a footnote requires it to be on a separate line, so this reformatting breaks the footnote.

Steps to Reproduce

See above

🐛 BUG: `Error: unhandled node type: 'expression'`

Describe the Bug

A reduced example of code that triggers the bug is:

{true && <div {...{ style: 'color:red;'}}>hello</div>}

which result in this error message:

["ERROR" - 4:44:37 PM] Unhandled node type "expression"!
Error: Unhandled node type "expression"!

Steps to Reproduce

Format the above code snippet w/ prettier plugin version 0.1.0-next.4

🐛 BUG: the plugin throws `(0 , import__2.default) is not a function` when using JavaScript `map`, forEach`, etc...

Describe the Bug

When you add any JavaScript expression like map, forEach in an .astro file, the formatter plugin throws an error ParseError: (0 , import__2.default) is not a function.

For example, when you add

const items = ["Dog", "Cat", "Platipus"];

<ul>
  {items.map((item) => (
    <li>{item}</li>
  ))}
</ul>

you get this error.

The above example come https://docs.astro.build/en/core-concepts/astro-components/#dynamic-jsx-expressions.

Steps to Reproduce

  1. Go to https://stackblitz.com/edit/github-wanuv4-s6gjnn?file=src%2Fpages%2Findex.astro
  2. Run npm run format and check the console output

🐛 BUG: Does not work using global install of prettier-plugin-astro

Describe the Bug

I have "prettier-plugin-astro" installed only globally (via "npm install -g prettier-plugin-astro") instead of within the local package.

VSCode doesn't seem to format files if it's only installed globally. If I install "prettier-plugin-astro" locally into the current npm project, then it works.

Steps to Reproduce

  1. Install the plugin globally: npm install -g prettier-plugin-astro
  2. Configure the VSCode plugin per the instructions
  3. Try to format and it doesn't work

If you install the plugin locally, then formatting works.

🐛 BUG: Label element split onto next line

Describe the Bug

Some instances of <label> are incorrectly formated with the closing %gt; being placed on a nerw line

---
---
<label
  >Text:
  <span>ss</span>
</label>

Steps to Reproduce

Astro file as follows and run npx prettier -w .as described in the README

---
---
<label>Text:
  <span>ss</span>
</label>

🐛 BUG: <style> is moved inside <body> where applicable

Describe the Bug

Apologies if this is not a bug, but:

In components/layouts with ..., component/layout styles written outside the main html are moved by the plugin to inside the ... structure.

example

Steps to Reproduce

I expect the following code to remain as is:

<body>
<div>
bla bla bla
</div>
</body>

<style>
div {
  border: 1px solid red;
  }
</style>

but instead, the prettier plugin changes it to:

<body>
  <div>bla bla bla</div>
  <style>
    div {
      border: 1px solid red;
    }
  </style>
</body>

🐛 BUG: Expression inside markdown files and the Astro markdown component

Describe the Bug

Currently, the plugin doesn't format markdown files we leave that to Prettier. But we need to take over the default markdown formatter from Prettier to be able to format expressions.

Steps to Reproduce

Input:

{frontmatter.value * 2} 

Output:

{frontmatter.value \* 2} 

Expected output:

{frontmatter.value * 2} 

🐛 BUG: Components inside `<Markdown>` are deleted by formatting the document

Describe the Bug

Components inside <Markdown> are deleted by formatting the document

Steps to Reproduce

Describe the Bug

Astro prettier plugin deletes components inside a Markdown component

Steps to Reproduce

  • Install astro prettier plugin

  • Copy-and-paste below input into a new .astro file

  • Format document

  • Expected: Inner component remains

  • Seen: Inner component deleted

Input document:

---
---



<div>
  <Markdown>
  <Fragment>test</Fragment>
  </Markdown>
</div>

After format document:

---
---



<div>
  <Markdown></Markdown>
</div>

🐛 BUG: Attribute spread breaks prettier plugin

Describe the Bug

Error processing a file with an attribute spread:

$ ./node_modules/.bin/prettier --loglevel debug --no-editorconfig --parser astro test.astro                               
[debug] normalized argv: {"_":["test.astro"],"color":true,"editorconfig":false,"loglevel":"debug","parser":"astro","config-precedence":"cli-override","debug-repeat":0,"ignore-path":".prettierignore","plugin":[],"plugin-search-dir":[]}
[debug] resolve config from 'test.astro'
[debug] loaded options `{"tabWidth":2,"useTabs":false,"printWidth":160}`
[debug] applied config-precedence (cli-override): {"parser":"astro","printWidth":160,"tabWidth":2,"useTabs":false}
test.astro[error] test.astro: Error: Unhandled node type "Spread"!
[error]     at Object.print (/[redacted]/node_modules/prettier-plugin-astro/dist/index.js:712:19)
[error]     at callPluginPrintFunction (/[redacted]/node_modules/prettier/index.js:13787:21)
[error]     at mainPrintInternal (/[redacted]/node_modules/prettier/index.js:13725:17)
[error]     at mainPrint (/[redacted]/node_modules/prettier/index.js:13707:14)
[error]     at /[redacted]/node_modules/prettier/index.js:13495:23
[error]     at AstPath.each (/[redacted]/node_modules/prettier/index.js:13482:7)
[error]     at AstPath.map (/[redacted]/node_modules/prettier/index.js:13494:10)
[error]     at Object.print (/[redacted]/node_modules/prettier-plugin-astro/dist/index.js:565:37)
[error]     at callPluginPrintFunction (/[redacted]/node_modules/prettier/index.js:13787:21)
[error]     at mainPrintInternal (/[redacted]/node_modules/prettier/index.js:13725:17)

with test.astro as follows:

<div {...Astro.props}><div /></div>

Steps to Reproduce

See above

🐛 BUG: style tag don't get line wrap

Describe the Bug

When saving in a file, this plugin removes all line breaks in the style tag when not top level:

image

Here is a picture of the same Astro code when with the style tag top level:

image

Steps to Reproduce

  1. install "prettier-plugin-astro": "^0.0.9"
  2. in an Astro file, create an style tag (with or without lang prop) and put some styles in it
  3. save

🐛 BUG: JSX style comment in Markdown

Describe the Bug

Prettier is transforming
{/* into {/\*
Also, JSX comments aren't taken into account by the AST / Syntax highlighter, still in Markdown.
They just behave as plain text (but that's another issue)

🐛 BUG: Attributes names are removed

Describe the Bug

Attribute names are removed from component usage in .astro files:

Original code:

<BaseHead title={title} description={description} permalink={permalink} />

Formatted to:

<BaseHead {title} {description} {permalink} />

Expected:

<BaseHead title={title} description={description} permalink={permalink} />

This shorter syntax seems to work but there are two problems with it:

  1. It isn't documented
  2. It doesn't work with TypeScript
    image

Steps to Reproduce

See code formatting examples above

💡 RFC: Test on Windows and macOS in addition to Linux

Background & Motivation

I want to make sure our tests pass on all supported OS's as discussed in #48

Proposed Solution

Possible solutions

Copy the test action from the main Astro repo

Alternatives considered

Risks, downsides, and/or tradeoffs

Open Questions

Detailed Design

No response

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.