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

Fixes an error that occurs when non-script syntax is written in a script tag in HTML #45

Merged
merged 2 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
66 changes: 50 additions & 16 deletions src/packages/core-parts/finder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,30 +379,64 @@ export function findTargetBraceNodesForHtml(
value: z.string(),
}),
),
attrs: z.array(
z.object({
name: z.string(),
value: z.unknown(),
}),
),
}),
) &&
node.name === 'script'
) {
const textNodeInScript = node.children.at(0);

if (addon.parseTypescript && textNodeInScript) {
const openingTagEndingOffset = node.startSourceSpan.end.offset;
if (node.attrs.find((attr) => attr.name === 'lang' && attr.value === 'ts')) {
if (addon.parseTypescript && textNodeInScript) {
const openingTagEndingOffset = node.startSourceSpan.end.offset;

const typescriptAst = addon.parseTypescript(textNodeInScript.value, {
...options,
parser: 'typescript',
});
const targetBraceNodesInScript = findTargetBraceNodes(typescriptAst).map<BraceNode>(
({ type, range: [braceNodeRangeStart, braceNodeRangeEnd] }) => ({
type,
range: [
braceNodeRangeStart + openingTagEndingOffset,
braceNodeRangeEnd + openingTagEndingOffset,
],
}),
);
const typescriptAst = addon.parseTypescript(textNodeInScript.value, {
...options,
parser: 'typescript',
});
const targetBraceNodesInScript = findTargetBraceNodes(typescriptAst).map<BraceNode>(
({ type, range: [braceNodeRangeStart, braceNodeRangeEnd] }) => ({
type,
range: [
braceNodeRangeStart + openingTagEndingOffset,
braceNodeRangeEnd + openingTagEndingOffset,
],
}),
);

braceNodes.push(...targetBraceNodesInScript);
braceNodes.push(...targetBraceNodesInScript);
}
} else if (
node.attrs.find(
(attr) =>
attr.name === 'type' && (attr.value === '' || attr.value === 'text/javascript'),
) ||
!node.attrs.find((attr) => attr.name === 'type')
) {
if (addon.parseBabel && textNodeInScript) {
const openingTagEndingOffset = node.startSourceSpan.end.offset;

const babelAst = addon.parseBabel(textNodeInScript.value, {
...options,
parser: 'babel',
});
const targetBraceNodesInScript = findTargetBraceNodes(babelAst).map<BraceNode>(
({ type, range: [braceNodeRangeStart, braceNodeRangeEnd] }) => ({
type,
range: [
braceNodeRangeStart + openingTagEndingOffset,
braceNodeRangeEnd + openingTagEndingOffset,
],
}),
);

braceNodes.push(...targetBraceNodesInScript);
}
}
}
break;
Expand Down
26 changes: 26 additions & 0 deletions tests/v2-test/html/script-tag/1tbs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { format } from 'prettier';
import { baseOptions } from 'test-settings';
import { expect, test } from 'vitest';

// eslint-disable-next-line import/no-extraneous-dependencies
import * as thisPlugin from '@/packages/v2-plugin';

import { fixtures } from './fixtures';

const options = {
...baseOptions,
plugins: [thisPlugin],
parser: 'html',
braceStyle: '1tbs',
};

for (const fixture of fixtures) {
test(fixture.name, () => {
expect(
format(fixture.input, {
...options,
...(fixture.options ?? {}),
}),
).toMatchSnapshot();
});
}
48 changes: 48 additions & 0 deletions tests/v2-test/html/script-tag/__snapshots__/1tbs.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`(1) If a script tag has no type, it is assumed to contain JavaScript code. 1`] = `
"<script>
export function Foo({ children }) {
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(2) If a script tag has an empty type, it is assumed to contain JavaScript code. 1`] = `
"<script type="">
export function Foo({ children }) {
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(3) If a script tag has a type of \`text/javascript\`, it is assumed to contain JavaScript code. 1`] = `
"<script type="text/javascript">
export function Foo({ children }) {
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(4) If a script tag has a \`lang\` attribute whose value is \`ts\`, it is assumed to contain TypeScript code, regardless of type. 1`] = `
"<script lang="ts" type="anything">
interface MyInterface {
foo(): string;
bar: Array<number>;
}
</script>
"
`;

exports[`(5) Otherwise, leave Prettier's output as is. 1`] = `
"<script type="application/json">
{
"lorem": "ipsum",
"dolor": "sit amet"
}
</script>
"
`;
52 changes: 52 additions & 0 deletions tests/v2-test/html/script-tag/__snapshots__/allman.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`(1) If a script tag has no type, it is assumed to contain JavaScript code. 1`] = `
"<script>
export function Foo({ children })
{
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(2) If a script tag has an empty type, it is assumed to contain JavaScript code. 1`] = `
"<script type="">
export function Foo({ children })
{
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(3) If a script tag has a type of \`text/javascript\`, it is assumed to contain JavaScript code. 1`] = `
"<script type="text/javascript">
export function Foo({ children })
{
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(4) If a script tag has a \`lang\` attribute whose value is \`ts\`, it is assumed to contain TypeScript code, regardless of type. 1`] = `
"<script lang="ts" type="anything">
interface MyInterface
{
foo(): string;
bar: Array<number>;
}
</script>
"
`;

exports[`(5) Otherwise, leave Prettier's output as is. 1`] = `
"<script type="application/json">
{
"lorem": "ipsum",
"dolor": "sit amet"
}
</script>
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`(1) If a script tag has no type, it is assumed to contain JavaScript code. 1`] = `
"<script>
export function Foo({ children }) {
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(2) If a script tag has an empty type, it is assumed to contain JavaScript code. 1`] = `
"<script type="">
export function Foo({ children }) {
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(3) If a script tag has a type of \`text/javascript\`, it is assumed to contain JavaScript code. 1`] = `
"<script type="text/javascript">
export function Foo({ children }) {
return <div className="lorem ipsum dolor sit amet">{children}</div>;
}
</script>
"
`;

exports[`(4) If a script tag has a \`lang\` attribute whose value is \`ts\`, it is assumed to contain TypeScript code, regardless of type. 1`] = `
"<script lang="ts" type="anything">
interface MyInterface {
foo(): string;
bar: Array<number>;
}
</script>
"
`;

exports[`(5) Otherwise, leave Prettier's output as is. 1`] = `
"<script type="application/json">
{
"lorem": "ipsum",
"dolor": "sit amet"
}
</script>
"
`;
26 changes: 26 additions & 0 deletions tests/v2-test/html/script-tag/allman.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { format } from 'prettier';
import { baseOptions } from 'test-settings';
import { expect, test } from 'vitest';

// eslint-disable-next-line import/no-extraneous-dependencies
import * as thisPlugin from '@/packages/v2-plugin';

import { fixtures } from './fixtures';

const options = {
...baseOptions,
plugins: [thisPlugin],
parser: 'html',
braceStyle: 'allman',
};

for (const fixture of fixtures) {
test(fixture.name, () => {
expect(
format(fixture.input, {
...options,
...(fixture.options ?? {}),
}),
).toMatchSnapshot();
});
}
70 changes: 70 additions & 0 deletions tests/v2-test/html/script-tag/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Fixture } from 'test-settings';

export const fixtures: Omit<Fixture, 'output'>[] = [
{
name: '(1) If a script tag has no type, it is assumed to contain JavaScript code.',
input: `
<script>
export function Foo({ children }) {
return (
<div className="lorem ipsum dolor sit amet">
{children}
</div>
);
}
</script>
`,
options: {},
},
{
name: '(2) If a script tag has an empty type, it is assumed to contain JavaScript code.',
input: `
<script type="">
export function Foo({ children }) {
return (
<div className="lorem ipsum dolor sit amet">
{children}
</div>
);
}
</script>
`,
options: {},
},
{
name: '(3) If a script tag has a type of `text/javascript`, it is assumed to contain JavaScript code.',
input: `
<script type="text/javascript">
export function Foo({ children }) {
return (
<div className="lorem ipsum dolor sit amet">
{children}
</div>
);
}
</script>
`,
options: {},
},
{
name: '(4) If a script tag has a `lang` attribute whose value is `ts`, it is assumed to contain TypeScript code, regardless of type.',
input: `
<script lang="ts" type="anything">
interface MyInterface { foo(): string, bar: Array<number> }
</script>
`,
options: {},
},
{
name: "(5) Otherwise, leave Prettier's output as is.",
input: `
<script type="application/json">
{
lorem: "ipsum",
'dolor': 'sit amet',
}
</script>
`,
options: {},
},
];
26 changes: 26 additions & 0 deletions tests/v2-test/html/script-tag/stroustrup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { format } from 'prettier';
import { baseOptions } from 'test-settings';
import { expect, test } from 'vitest';

// eslint-disable-next-line import/no-extraneous-dependencies
import * as thisPlugin from '@/packages/v2-plugin';

import { fixtures } from './fixtures';

const options = {
...baseOptions,
plugins: [thisPlugin],
parser: 'html',
braceStyle: 'stroustrup',
};

for (const fixture of fixtures) {
test(fixture.name, () => {
expect(
format(fixture.input, {
...options,
...(fixture.options ?? {}),
}),
).toMatchSnapshot();
});
}
Loading
Loading